[ONOS-6348] Intent installer redesign

Change-Id: I9ae2e8158dc1c686eaf848f330566f9dbb78405f
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/InstallCoordinatorTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/InstallCoordinatorTest.java
new file mode 100644
index 0000000..2bdf200
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/InstallCoordinatorTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestTools;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.TestInstallableIntent;
+import org.onosproject.store.intent.impl.IntentStoreAdapter;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.IntStream;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for install coordinator.
+ */
+public class InstallCoordinatorTest {
+    private static final int INSTALL_DELAY = 100;
+    private static final int INSTALL_DURATION = 1000;
+    private static final IdGenerator ID_GENERATOR = new MockIdGenerator();
+
+    private InstallCoordinator installCoordinator;
+    private InstallerRegistry installerRegistry;
+    private TestIntentStore intentStore;
+    private TestIntentInstaller intentInstaller;
+
+
+    @Before
+    public void setup() {
+        Intent.unbindIdGenerator(ID_GENERATOR);
+        Intent.bindIdGenerator(ID_GENERATOR);
+        installerRegistry = new InstallerRegistry();
+        intentStore = new TestIntentStore();
+        intentInstaller = new TestIntentInstaller();
+        installerRegistry.registerInstaller(TestInstallableIntent.class, intentInstaller);
+
+        installCoordinator = new InstallCoordinator(installerRegistry, intentStore);
+    }
+
+    @After
+    public void tearDown() {
+        installerRegistry.unregisterInstaller(TestInstallableIntent.class);
+        Intent.unbindIdGenerator(ID_GENERATOR);
+    }
+
+    /**
+     * Installs test Intents.
+     */
+    @Test
+    public void testInstallIntent() {
+        IntentData toInstall = new IntentData(createTestIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        List<Intent> intents = Lists.newArrayList();
+        IntStream.range(0, 10).forEach(val -> {
+            intents.add(new TestInstallableIntent(val));
+        });
+        toInstall = new IntentData(toInstall, intents);
+        installCoordinator.installIntents(Optional.empty(), Optional.of(toInstall));
+        Intent toInstallIntent = toInstall.intent();
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toInstallIntent, newData.intent());
+            assertEquals(IntentState.INSTALLED, newData.state());
+            assertEquals(intents, newData.installables());
+        });
+    }
+
+    /**
+     * Uninstalls test Intents.
+     */
+    @Test
+    public void testUninstallIntent() {
+        IntentData toUninstall = new IntentData(createTestIntent(),
+                                              IntentState.WITHDRAWING,
+                                              new WallClockTimestamp());
+        List<Intent> intents = Lists.newArrayList();
+
+        IntStream.range(0, 10).forEach(val -> {
+            intents.add(new TestInstallableIntent(val));
+        });
+
+        toUninstall = new IntentData(toUninstall, intents);
+
+        installCoordinator.installIntents(Optional.of(toUninstall), Optional.empty());
+        Intent toUninstallIntent = toUninstall.intent();
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toUninstallIntent, newData.intent());
+            assertEquals(IntentState.WITHDRAWN, newData.state());
+            assertEquals(ImmutableList.of(), newData.installables());
+        });
+
+    }
+
+    /**
+     * Do both uninstall and install test Intents.
+     */
+    @Test
+    public void testUninstallAndInstallIntent() {
+        IntentData toUninstall = new IntentData(createTestIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        IntentData toInstall = new IntentData(createTestIntent(),
+                                                IntentState.INSTALLING,
+                                                new WallClockTimestamp());
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+
+        IntStream.range(0, 10).forEach(val -> {
+            intentsToUninstall.add(new TestInstallableIntent(val));
+        });
+
+        IntStream.range(10, 20).forEach(val -> {
+            intentsToInstall.add(new TestInstallableIntent(val));
+        });
+
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+        installCoordinator.installIntents(Optional.of(toUninstall), Optional.of(toInstall));
+        Intent toInstallIntent = toInstall.intent();
+
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toInstallIntent, newData.intent());
+            assertEquals(IntentState.INSTALLED, newData.state());
+            assertEquals(intentsToInstall, newData.installables());
+        });
+    }
+
+    /**
+     * Not uninstall nor install anything.
+     */
+    @Test
+    public void testInstallNothing() {
+        installCoordinator.installIntents(Optional.empty(), Optional.empty());
+        assertNull(intentStore.newData);
+    }
+
+    /**
+     * Test Intent install failed.
+     */
+    @Test
+    public void testInstallFailed() {
+        installerRegistry.unregisterInstaller(TestInstallableIntent.class);
+        installerRegistry.registerInstaller(TestInstallableIntent.class, new TestFailedIntentInstaller());
+        IntentData toUninstall = new IntentData(createTestIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        IntentData toInstall = new IntentData(createTestIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+
+        IntStream.range(0, 10).forEach(val -> {
+            intentsToUninstall.add(new TestInstallableIntent(val));
+        });
+
+        IntStream.range(10, 20).forEach(val -> {
+            intentsToInstall.add(new TestInstallableIntent(val));
+        });
+
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+        installCoordinator.installIntents(Optional.of(toUninstall), Optional.of(toInstall));
+
+        Intent toUninstallIntent = toUninstall.intent();
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toUninstallIntent, newData.intent());
+            assertEquals(IntentState.CORRUPT, newData.state());
+            assertEquals(intentsToUninstall, newData.installables());
+        });
+    }
+
+    /**
+     * Creates a test Intent.
+     *
+     * @return the test Intent.
+     */
+    private PointToPointIntent createTestIntent() {
+        FilteredConnectPoint ingress = new FilteredConnectPoint(ConnectPoint.deviceConnectPoint("s1/1"));
+        FilteredConnectPoint egress = new FilteredConnectPoint(ConnectPoint.deviceConnectPoint("s1/2"));
+        ApplicationId appId = TestApplicationId.create("test App");
+        return PointToPointIntent.builder()
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .appId(appId)
+                .key(Key.of("Test key", appId))
+                .build();
+    }
+
+    /**
+     * Test Intent store; records the newest Intent data.
+     */
+    class TestIntentStore extends IntentStoreAdapter {
+        IntentData newData;
+        @Override
+        public void write(IntentData newData) {
+            this.newData = newData;
+        }
+    }
+
+    /**
+     * Test Intent installer; always success for every Intent operation.
+     */
+    class TestIntentInstaller implements IntentInstaller<TestInstallableIntent> {
+        @Override
+        public void apply(IntentOperationContext<TestInstallableIntent> context) {
+            installCoordinator.success(context);
+        }
+    }
+
+    /**
+     * Test Intent installer; always failed for every Intent operation.
+     */
+    class TestFailedIntentInstaller implements IntentInstaller<TestInstallableIntent> {
+        @Override
+        public void apply(IntentOperationContext<TestInstallableIntent> context) {
+            installCoordinator.failed(context);
+        }
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
index 585d3a0..45c90e7 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
@@ -33,6 +33,9 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.impl.TestCoreManager;
 import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompilationException;
@@ -42,7 +45,10 @@
 import org.onosproject.net.intent.IntentEvent.Type;
 import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentInstaller;
 import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentOperationContext;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
@@ -93,8 +99,11 @@
 
     protected IntentService service;
     protected IntentExtensionService extensionService;
+    protected IntentInstallCoordinator intentInstallCoordinator;
     protected TestListener listener = new TestListener();
     protected TestIntentCompiler compiler = new TestIntentCompiler();
+    protected TestIntentInstaller installer;
+    protected TestIntentTracker trackerService = new TestIntentTracker();
 
     private static class TestListener implements IntentListener {
         final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
@@ -217,6 +226,56 @@
         }
     }
 
+    public static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
+
+        protected IntentExtensionService intentExtensionService;
+        protected ObjectiveTrackerService trackerService;
+        protected IntentInstallCoordinator intentInstallCoordinator;
+        protected FlowRuleService flowRuleService;
+
+        public TestIntentInstaller(IntentExtensionService intentExtensionService,
+                                   ObjectiveTrackerService trackerService,
+                                   IntentInstallCoordinator intentInstallCoordinator,
+                                   FlowRuleService flowRuleService) {
+            this.intentExtensionService = intentExtensionService;
+            this.trackerService = trackerService;
+            this.intentInstallCoordinator = intentInstallCoordinator;
+            this.flowRuleService = flowRuleService;
+        }
+
+        @Override
+        public void apply(IntentOperationContext<MockInstallableIntent> context) {
+            List<MockInstallableIntent> uninstallIntents = context.intentsToUninstall();
+            List<MockInstallableIntent> installIntents = context.intentsToInstall();
+
+            FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+
+            uninstallIntents.stream()
+                    .map(FlowRuleIntent::flowRules)
+                    .flatMap(Collection::stream)
+                    .forEach(builder::remove);
+
+            installIntents.stream()
+                    .map(FlowRuleIntent::flowRules)
+                    .flatMap(Collection::stream)
+                    .forEach(builder::add);
+
+            FlowRuleOperationsContext ctx = new FlowRuleOperationsContext() {
+                @Override
+                public void onSuccess(FlowRuleOperations ops) {
+                    intentInstallCoordinator.intentInstallSuccess(context);
+                }
+
+                @Override
+                public void onError(FlowRuleOperations ops) {
+                    intentInstallCoordinator.intentInstallFailed(context);
+                }
+            };
+
+            flowRuleService.apply(builder.build(ctx));
+        }
+    }
+
     private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
         return new EntryForIntentMatcher(id);
     }
@@ -227,17 +286,24 @@
         flowRuleService = new MockFlowRuleService();
         manager.store = new SimpleIntentStore();
         injectEventDispatcher(manager, new TestEventDispatcher());
-        manager.trackerService = new TestIntentTracker();
+        manager.trackerService = trackerService;
         manager.flowRuleService = flowRuleService;
         manager.coreService = new TestCoreManager();
         manager.configService = mock(ComponentConfigService.class);
         service = manager;
         extensionService = manager;
+        intentInstallCoordinator = manager;
+
 
         manager.activate();
         service.addListener(listener);
         extensionService.registerCompiler(MockIntent.class, compiler);
 
+        installer = new TestIntentInstaller(extensionService, trackerService,
+                                            intentInstallCoordinator, flowRuleService);
+
+        extensionService.registerInstaller(MockInstallableIntent.class, installer);
+
         assertTrue("store should be empty",
                    Sets.newHashSet(service.getIntents()).isEmpty());
         assertEquals(0L, flowRuleService.getFlowRuleCount());
@@ -269,6 +335,7 @@
     @After
     public void tearDown() {
         extensionService.unregisterCompiler(MockIntent.class);
+        extensionService.unregisterInstaller(MockInstallableIntent.class);
         service.removeListener(listener);
         manager.deactivate();
         // TODO null the other refs?
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/AbstractIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/AbstractIntentInstallerTest.java
new file mode 100644
index 0000000..caa07be
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/AbstractIntentInstallerTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.installer;
+
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.impl.ObjectiveTrackerService;
+
+import static org.easymock.EasyMock.createMock;
+
+/**
+ * Abstract class to hold the common variables and pieces of code for Intent
+ * installer test.
+ */
+public class AbstractIntentInstallerTest {
+    protected static final ApplicationId APP_ID = TestApplicationId.create("IntentInstallerTest");
+    protected static final ConnectPoint CP1 = ConnectPoint.deviceConnectPoint("s1/1");
+    protected static final ConnectPoint CP2 = ConnectPoint.deviceConnectPoint("s1/2");
+    protected static final ConnectPoint CP3 = ConnectPoint.deviceConnectPoint("s1/3");
+    protected static final Key KEY1 = Key.of("test intent 1", APP_ID);
+    protected static final ResourceGroup RG1 = ResourceGroup.of("test resource group 1");
+    protected static final int DEFAULT_PRIORITY = 30000;
+    protected static final IdGenerator ID_GENERATOR = new MockIdGenerator();
+
+    protected IntentExtensionService intentExtensionService;
+    protected ObjectiveTrackerService trackerService;
+    protected TestIntentInstallCoordinator intentInstallCoordinator;
+
+    public void setup() {
+        Intent.unbindIdGenerator(ID_GENERATOR);
+        Intent.bindIdGenerator(ID_GENERATOR);
+        intentExtensionService = createMock(IntentExtensionService.class);
+        trackerService = createMock(ObjectiveTrackerService.class);
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+    }
+
+    public void tearDown() {
+        Intent.unbindIdGenerator(ID_GENERATOR);
+    }
+
+    /**
+     * Creates point to point Intent for test.
+     *
+     * @return the point to point Intent
+     */
+    public PointToPointIntent createP2PIntent() {
+        PointToPointIntent intent;
+        TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+        FilteredConnectPoint ingress = new FilteredConnectPoint(CP1);
+        FilteredConnectPoint egress = new FilteredConnectPoint(CP2);
+
+        intent = PointToPointIntent.builder()
+                .selector(selector)
+                .treatment(treatment)
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .appId(APP_ID)
+                .build();
+
+        return intent;
+    }
+
+    /**
+     * The Intent install coordinator for test.
+     * Records success and fail context.
+     */
+    class TestIntentInstallCoordinator implements IntentInstallCoordinator {
+
+        IntentOperationContext successContext;
+        IntentOperationContext failedContext;
+
+        @Override
+        public void intentInstallSuccess(IntentOperationContext context) {
+            successContext = context;
+        }
+
+        @Override
+        public void intentInstallFailed(IntentOperationContext context) {
+            failedContext = context;
+        }
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/DomainIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/DomainIntentInstallerTest.java
new file mode 100644
index 0000000..2542322
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/DomainIntentInstallerTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.installer;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.domain.DomainIntent;
+import org.onosproject.net.domain.DomainIntentOperations;
+import org.onosproject.net.domain.DomainIntentService;
+import org.onosproject.net.domain.DomainPointToPointIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for domain Intent installer.
+ */
+public class DomainIntentInstallerTest extends AbstractIntentInstallerTest {
+    protected DomainIntentInstaller installer;
+    protected TestDomainIntentService domainIntentService;
+
+    @Before
+    public void setup() {
+        super.setup();
+        domainIntentService = new TestDomainIntentService();
+        installer = new DomainIntentInstaller();
+        installer.domainIntentService = domainIntentService;
+        installer.trackerService = trackerService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+
+        installer.activated();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs domain Intents.
+     */
+    @Test
+    public void testInstall() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createDomainIntents();
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Uninstall domain Intents.
+     */
+    @Test
+    public void testUninstall() {
+        List<Intent> intentsToUninstall = createDomainIntents();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.WITHDRAWING,
+                                                new WallClockTimestamp());
+        IntentData toInstall = null;
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Do both uninstall and install domain Intents.
+     */
+    @Test
+    public void testUninstallAndInstall() {
+        List<Intent> intentsToUninstall = createDomainIntents();
+        List<Intent> intentsToInstall = createAnotherDomainIntents();
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Test if domain Intent installation operations failed.
+     */
+    @Test
+    public void testInstallFailed() {
+        domainIntentService = new TestFailedDomainIntentService();
+        installer.domainIntentService = domainIntentService;
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createDomainIntents();
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.failedContext, operationContext);
+    }
+
+    /**
+     * Creates domain Intents.
+     *
+     * @return the domain Intents
+     */
+    private List<Intent> createDomainIntents() {
+        FilteredConnectPoint ingress = new FilteredConnectPoint(CP1);
+        FilteredConnectPoint egress = new FilteredConnectPoint(CP2);
+        DomainPointToPointIntent intent = DomainPointToPointIntent.builder()
+                .appId(APP_ID)
+                .key(KEY1)
+                .priority(DEFAULT_PRIORITY)
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .links(ImmutableList.of())
+                .build();
+
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Create another domain Intents.
+     *
+     * @return the domain Intents
+     */
+    private List<Intent> createAnotherDomainIntents() {
+        FilteredConnectPoint ingress = new FilteredConnectPoint(CP1);
+        FilteredConnectPoint egress = new FilteredConnectPoint(CP3);
+        DomainPointToPointIntent intent = DomainPointToPointIntent.builder()
+                .appId(APP_ID)
+                .key(KEY1)
+                .priority(DEFAULT_PRIORITY)
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .links(ImmutableList.of())
+                .build();
+
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Test domain Intent service; always success for all domain Intent operations.
+     */
+    class TestDomainIntentService implements DomainIntentService {
+        @Override
+        public void sumbit(DomainIntentOperations domainOperations) {
+            domainOperations.callback().onSuccess(domainOperations);
+        }
+    }
+
+    /**
+     * Test domain Intent service; always failed of any domain Intent operation.
+     */
+    class TestFailedDomainIntentService extends TestDomainIntentService {
+
+        @Override
+        public void sumbit(DomainIntentOperations domainOperations) {
+            domainOperations.callback().onError(domainOperations);
+        }
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstallerTest.java
new file mode 100644
index 0000000..99f5db9
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstallerTest.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.installer;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.intent.FlowObjectiveIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.flowobjective.ObjectiveError.*;
+
+/**
+ * Tests for flow objective Intent installer.
+ */
+public class FlowObjectiveIntentInstallerTest extends AbstractIntentInstallerTest {
+    private static final int NEXT_ID_1 = 1;
+    protected FlowObjectiveIntentInstaller installer;
+    protected TestFlowObjectiveService flowObjectiveService;
+
+    @Before
+    public void setup() {
+        super.setup();
+        flowObjectiveService = new TestFlowObjectiveService();
+        installer = new FlowObjectiveIntentInstaller();
+        installer.flowObjectiveService = flowObjectiveService;
+        installer.trackerService = trackerService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+
+        installer.activate();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs flow objective Intents.
+     */
+    @Test
+    public void testInstallIntent() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowObjectiveIntents();
+
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Uninstalls flow objective Intents.
+     */
+    @Test
+    public void testUninstallIntent() {
+        List<Intent> intentsToUninstall = createFlowObjectiveIntents();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+
+
+        IntentData toInstall = null;
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                              IntentState.WITHDRAWING,
+                                              new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Do both uninstall and install flow objective Intents.
+     */
+    @Test
+    public void testUninstallAndInstallIntent() {
+        List<Intent> intentsToUninstall = createFlowObjectiveIntents();
+        List<Intent> intentsToInstall = createAnotherFlowObjectiveIntents();
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /*
+     * Error handling
+     */
+    IntentOperationContext context;
+    IntentOperationContext failedContext;
+    IntentOperationContext successContext;
+    List<ObjectiveError> errors;
+
+    /**
+     * Handles UNSUPPORTED error.
+     */
+    @Test
+    public void testUnsupportedError() {
+        // Unsupported, should just failed
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService();
+        context = createInstallContext();
+        installer.apply(context);
+        assertEquals(intentInstallCoordinator.failedContext, context);
+    }
+
+    /**
+     * Handles FLOWINSTALLATIONFAILED error with touch the threshold.
+     */
+    @Test
+    public void testFlowInstallationFailedError() {
+        // flow install failed, should retry until retry threshold
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED,
+                                  FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED,
+                                  FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED,
+                                  FLOWINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        failedContext = intentInstallCoordinator.failedContext;
+        assertEquals(failedContext, context);
+    }
+
+    /**
+     * Handles FLOWINSTALLATIONFAILED error without touch the threshold.
+     */
+    @Test
+    public void testFlowInstallationFailedErrorUnderThreshold() {
+        // And retry two times and success
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPINSTALLATIONFAILED error with touch the threshold.
+     */
+    @Test
+    public void testGroupInstallationFailedError() {
+        // Group install failed, and retry threshold exceed
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED,
+                                  GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED,
+                                  GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED,
+                                  GROUPINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        failedContext = intentInstallCoordinator.failedContext;
+        assertEquals(failedContext, context);
+
+    }
+
+    /**
+     * Handles GROUPINSTALLATIONFAILED error without touch the threshold.
+     */
+    @Test
+    public void testGroupInstallationFailedErrorUnderThreshold() {
+        // group install failed, and retry two times.
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+
+    }
+
+    /**
+     * Handles GROUPEXISTS error.
+     */
+    @Test
+    public void testGroupExistError() {
+        // group exists, retry by using add to exist
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPEXISTS);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPMISSING error with ADD_TO_EXIST operation.
+     */
+    @Test
+    public void testGroupMissingError() {
+        // group exist -> group missing -> add group
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPEXISTS, GROUPMISSING);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPMISSING error with ADD operation.
+     */
+    @Test
+    public void testGroupChainElementMissingError() {
+        // group chain element missing
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPMISSING);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPMISSING error with REMOVE operation.
+     */
+    @Test
+    public void testGroupAlreadyRemoved() {
+        // group already removed
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPMISSING);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createUninstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Creates Intent operation context for uninstall Intents.
+     *
+     * @return the context
+     */
+    private IntentOperationContext createUninstallContext() {
+        List<Intent> intentsToUninstall = createFlowObjectiveIntents();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        IntentData toInstall = null;
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        return new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+    }
+
+    /**
+     * Creates Intent operation context for install Intents.
+     *
+     * @return the context
+     */
+    private IntentOperationContext createInstallContext() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowObjectiveIntents();
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        return new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+    }
+
+    /**
+     * Creates flow objective Intents.
+     *
+     * @return the flow objective intents
+     */
+    private List<Intent> createFlowObjectiveIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FilteringObjective filt = DefaultFilteringObjective.builder()
+                .addCondition(selector.getCriterion(Criterion.Type.IN_PORT))
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .permit()
+                .add();
+
+        NextObjective next = DefaultNextObjective.builder()
+                .withMeta(selector)
+                .addTreatment(treatment)
+                .makePermanent()
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .withType(NextObjective.Type.SIMPLE)
+                .withId(NEXT_ID_1)
+                .add();
+
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .nextStep(NEXT_ID_1)
+                .add();
+
+        List<Objective> objectives = ImmutableList.of(filt, next, fwd);
+        List<DeviceId> deviceIds = ImmutableList.of(CP1.deviceId(), CP1.deviceId(), CP1.deviceId());
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        Intent intent = new FlowObjectiveIntent(APP_ID, KEY1, deviceIds, objectives, resources, RG1);
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Creates flow objective Intents with different selector.
+     *
+     * @return the flow objective Intents
+     */
+    private List<Intent> createAnotherFlowObjectiveIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VlanId.vlanId("100"))
+                .matchInPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FilteringObjective filt = DefaultFilteringObjective.builder()
+                .addCondition(selector.getCriterion(Criterion.Type.IN_PORT))
+                .addCondition(selector.getCriterion(Criterion.Type.VLAN_VID))
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .permit()
+                .add();
+
+        NextObjective next = DefaultNextObjective.builder()
+                .withMeta(selector)
+                .addTreatment(treatment)
+                .makePermanent()
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .withType(NextObjective.Type.SIMPLE)
+                .withId(NEXT_ID_1)
+                .add();
+
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .nextStep(NEXT_ID_1)
+                .add();
+
+        List<Objective> objectives = ImmutableList.of(filt, next, fwd);
+        List<DeviceId> deviceIds = ImmutableList.of(CP1.deviceId(), CP1.deviceId(), CP1.deviceId());
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        Intent intent = new FlowObjectiveIntent(APP_ID, KEY1, deviceIds, objectives, resources, RG1);
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Flow objective service for test; always successful for every flow objectives.
+     */
+    class TestFlowObjectiveService extends FlowObjectiveServiceAdapter {
+        List<DeviceId> devices = Lists.newArrayList();
+        List<Objective> objectives = Lists.newArrayList();
+
+        @Override
+        public void apply(DeviceId deviceId, Objective objective) {
+            devices.add(deviceId);
+            objectives.add(objective);
+            objective.context().ifPresent(context -> context.onSuccess(objective));
+        }
+    }
+
+    /**
+     * Flow objective service for test; contains errors for every flow objective
+     * submission.
+     */
+    class TestFailedFlowObjectiveService extends TestFlowObjectiveService {
+        private final Set<ObjectiveError> groupErrors =
+                ImmutableSet.of(GROUPEXISTS, GROUPINSTALLATIONFAILED,
+                                GROUPMISSING, GROUPREMOVALFAILED);
+
+        /**
+         * Error states to test error handler by given error queue
+         * e.g.
+         * FLOWINSTALLATIONFAILED -> FLOWINSTALLATIONFAILED -> null: should be success
+         * FLOWINSTALLATIONFAILED -> five same error -> ....       : should be failed
+         */
+        List<ObjectiveError> errors;
+
+        public TestFailedFlowObjectiveService() {
+            errors = Lists.newArrayList();
+            errors.add(UNSUPPORTED);
+        }
+
+        public TestFailedFlowObjectiveService(List<ObjectiveError> errors) {
+            this.errors = Lists.newArrayList(errors);
+        }
+
+        @Override
+        public void apply(DeviceId deviceId, Objective objective) {
+            if (errors.size() != 0) {
+                if (groupErrors.contains(errors.get(0)) && objective instanceof NextObjective) {
+                    ObjectiveError error = errors.remove(0);
+                    objective.context().ifPresent(context -> context.onError(objective, error));
+                    return;
+                }
+                if (!groupErrors.contains(errors.get(0)) && !(objective instanceof NextObjective)) {
+                    ObjectiveError error = errors.remove(0);
+                    objective.context().ifPresent(context -> context.onError(objective, error));
+                    return;
+                }
+            }
+            objective.context().ifPresent(context -> context.onSuccess(objective));
+
+        }
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
new file mode 100644
index 0000000..418392b
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.installer;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for flow rule Intent installer.
+ */
+public class FlowRuleIntentInstallerTest extends AbstractIntentInstallerTest {
+
+    private TestFlowRuleService flowRuleService;
+    private FlowRuleIntentInstaller installer;
+
+    @Before
+    public void setup() {
+        super.setup();
+        flowRuleService = new TestFlowRuleService();
+        installer = new FlowRuleIntentInstaller();
+        installer.flowRuleService = flowRuleService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        installer.trackerService = trackerService;
+
+        installer.activate();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs Intents only, no Intents to be uninstall.
+     */
+    @Test
+    public void testInstallOnly() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        Set<FlowRule> expectedFlowRules = intentsToInstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesAdd);
+    }
+
+    /**
+     * Uninstalls Intents only, no Intents to be install.
+     */
+    @Test
+    public void testUninstallOnly() {
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        List<Intent> intentsToUninstall = createFlowRuleIntents();
+
+        IntentData toInstall = null;
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                              IntentState.WITHDRAWING,
+                                              new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        Set<FlowRule> expectedFlowRules = intentsToUninstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesRemove);
+    }
+
+    /**
+     * Do both install and uninstall Intents with different flow rules.
+     */
+    @Test
+    public void testUninstallAndInstall() {
+        List<Intent> intentsToInstall = createAnotherFlowRuleIntents();
+        List<Intent> intentsToUninstall = createFlowRuleIntents();
+
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        Set<FlowRule> expectedFlowRules = intentsToUninstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesRemove);
+
+        expectedFlowRules = intentsToInstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesAdd);
+    }
+
+    /**
+     * Do both install and uninstall Intents with same flow rules.
+     */
+    @Test
+    public void testUninstallAndInstallUnchanged() {
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+        List<Intent> intentsToUninstall = createFlowRuleIntents();
+
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        assertEquals(0, flowRuleService.flowRulesRemove.size());
+        assertEquals(0, flowRuleService.flowRulesAdd.size());
+    }
+
+    /**
+     * Do both install and uninstall Intents with same flow rule Intent.
+     */
+    @Test
+    public void testUninstallAndInstallSame() {
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+        List<Intent> intentsToUninstall = intentsToInstall;
+
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        assertEquals(0, flowRuleService.flowRulesRemove.size());
+        assertEquals(0, flowRuleService.flowRulesAdd.size());
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        assertEquals(0, flowRuleService.flowRulesRemove.size());
+        assertEquals(0, flowRuleService.flowRulesAdd.size());
+    }
+
+    /**
+     * Test if the flow installation failed.
+     */
+    @Test
+    public void testFailed() {
+        installer.flowRuleService = new TestFailedFlowRuleService();
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext failedContext = intentInstallCoordinator.failedContext;
+        assertEquals(failedContext, operationContext);
+    }
+
+    /**
+     * Generates FlowRuleIntents for test.
+     *
+     * @return the FlowRuleIntents for test
+     */
+    public List<Intent> createFlowRuleIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPhyPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(CP1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        FlowRuleIntent intent = new FlowRuleIntent(APP_ID,
+                                                   KEY1,
+                                                   ImmutableList.of(flowRule),
+                                                   resources,
+                                                   PathIntent.ProtectionType.PRIMARY,
+                                                   RG1);
+
+        List<Intent> flowRuleIntents = Lists.newArrayList();
+        flowRuleIntents.add(intent);
+
+        return flowRuleIntents;
+    }
+
+    /**
+     * Generates another different FlowRuleIntents for test.
+     *
+     * @return the FlowRuleIntents for test
+     */
+    public List<Intent> createAnotherFlowRuleIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VlanId.vlanId("100"))
+                .matchInPhyPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(CP1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        FlowRuleIntent intent = new FlowRuleIntent(APP_ID,
+                                                   KEY1,
+                                                   ImmutableList.of(flowRule),
+                                                   resources,
+                                                   PathIntent.ProtectionType.PRIMARY,
+                                                   RG1);
+
+        List<Intent> flowRuleIntents = Lists.newArrayList();
+        flowRuleIntents.add(intent);
+
+        return flowRuleIntents;
+    }
+
+    /**
+     * The FlowRuleService for test; always success for any flow rule operations.
+     */
+    class TestFlowRuleService extends FlowRuleServiceAdapter {
+
+        Set<FlowRule> flowRulesAdd = Sets.newHashSet();
+        Set<FlowRule> flowRulesRemove = Sets.newHashSet();
+
+        public void record(FlowRuleOperations ops) {
+            flowRulesAdd.clear();
+            flowRulesRemove.clear();
+            ops.stages().forEach(stage -> {
+                stage.forEach(op -> {
+                    switch (op.type()) {
+                        case ADD:
+                            flowRulesAdd.add(op.rule());
+                            break;
+                        case REMOVE:
+                            flowRulesRemove.add(op.rule());
+                            break;
+                        default:
+                            break;
+                    }
+                });
+            });
+        }
+
+        @Override
+        public void apply(FlowRuleOperations ops) {
+            record(ops);
+            ops.callback().onSuccess(ops);
+        }
+    }
+
+    /**
+     * The FlowRuleService for test; always failed for any flow rule operations.
+     */
+    class TestFailedFlowRuleService extends TestFlowRuleService {
+        @Override
+        public void apply(FlowRuleOperations ops) {
+            record(ops);
+            ops.callback().onError(ops);
+        }
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstallerTest.java
new file mode 100644
index 0000000..53851da
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstallerTest.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.installer;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
+import org.onosproject.net.behaviour.protection.TransportEndpointDescription;
+import org.onosproject.net.config.BaseConfig;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.NetworkConfigServiceAdapter;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.ProtectionEndpointIntent;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for protection endpoint Intent installer.
+ */
+public class ProtectionEndpointIntentInstallerTest extends AbstractIntentInstallerTest {
+    private static final String FINGERPRINT = "Test fingerprint";
+    protected ProtectionEndpointIntentInstaller installer;
+    protected NetworkConfigService networkConfigService;
+    private static TestServiceDirectory directory;
+    private static ServiceDirectory original;
+
+    @BeforeClass
+    public static void setUpClass() throws TestUtils.TestUtilsException {
+        directory = new TestServiceDirectory();
+
+        CodecManager codecService = new CodecManager();
+        codecService.activate();
+        directory.add(CodecService.class, codecService);
+
+        // replace service directory used by BaseConfig
+        original = TestUtils.getField(BaseConfig.class, "services");
+        TestUtils.setField(BaseConfig.class, "services", directory);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws TestUtils.TestUtilsException {
+        TestUtils.setField(BaseConfig.class, "services", original);
+    }
+
+    @Before
+    public void setup() {
+        super.setup();
+        networkConfigService = new TestNetworkConfigService();
+        installer = new ProtectionEndpointIntentInstaller();
+        installer.networkConfigService = networkConfigService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        installer.trackerService = trackerService;
+
+        installer.activate();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs protection endpoint Intents.
+     * framework.
+     */
+    @Test
+    public void testInstallIntents() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createProtectionIntents(CP2);
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Uninstalls protection endpoint Intents.
+     * framework.
+     */
+    @Test
+    public void testUninstallIntents() {
+        List<Intent> intentsToUninstall = createProtectionIntents(CP2);
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLING,
+                                                new WallClockTimestamp());
+        IntentData toInstall = null;
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Test both uninstall and install protection endpoint Intents.
+     * framework.
+     */
+    @Test
+    public void testUninstallAndInstallIntents() {
+        List<Intent> intentsToUninstall = createProtectionIntents(CP2);
+        List<Intent> intentsToInstall = createProtectionIntents(CP3);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToInstall);
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Test if installation failed.
+     * framework.
+     */
+    @Test
+    public void testInstallFailed() {
+        networkConfigService = new TestFailedNetworkConfigService();
+        installer.networkConfigService = networkConfigService;
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createProtectionIntents(CP2);
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.failedContext, operationContext);
+    }
+
+    /**
+     * Creates protection endpoint Intents by givent output point.
+     *
+     * @param output the output point
+     * @return the protection endpoint Intents
+     */
+    private List<Intent> createProtectionIntents(ConnectPoint output) {
+        FilteredConnectPoint filteredOutput = new FilteredConnectPoint(output);
+        TransportEndpointDescription path = TransportEndpointDescription.builder()
+                .withOutput(filteredOutput)
+                .withEnabled(true).build();
+
+        List<TransportEndpointDescription> paths = ImmutableList.of(path);
+        ProtectedTransportEndpointDescription description =
+                ProtectedTransportEndpointDescription.of(paths, CP2.deviceId(), FINGERPRINT);
+        ProtectionEndpointIntent intent = ProtectionEndpointIntent.builder()
+                .appId(APP_ID)
+                .description(description)
+                .deviceId(CP1.deviceId())
+                .key(KEY1)
+                .resourceGroup(RG1)
+                .build();
+
+        return ImmutableList.of(intent);
+    }
+
+    class TestNetworkConfigService extends NetworkConfigServiceAdapter {
+        protected Set<NetworkConfigListener> listeners = Sets.newHashSet();
+
+        @Override
+        public void addListener(NetworkConfigListener listener) {
+            listeners.add(listener);
+        }
+
+        @Override
+        public void removeListener(NetworkConfigListener listener) {
+            listeners.remove(listener);
+        }
+
+        @Override
+        public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+            return null;
+        }
+
+        @Override
+        public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_REMOVED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+        }
+    }
+
+    /**
+     * Test network config service; will send wrong events to listeners.
+     */
+    class TestFailedNetworkConfigService extends TestNetworkConfigService {
+
+        @Override
+        public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_REMOVED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+            return null;
+        }
+
+        @Override
+        public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+        }
+    }
+}