Added test for ProgrammingServiceImpl 66/4066/4
authorMaros Marsalek <mmarsale@cisco.com>
Wed, 8 Jan 2014 10:57:44 +0000 (11:57 +0100)
committerMaros Marsalek <mmarsale@cisco.com>
Thu, 9 Jan 2014 16:06:12 +0000 (17:06 +0100)
Tested
-scheduling, canceling, executing and cleaning of transactions with or without dependencies
-timeout of instructions in different states with or without dependencies

Change-Id: I2fa5d592c31fe93227c535f2c7deb015aaf4f2e2
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
programming/impl/pom.xml
programming/impl/src/main/java/org/opendaylight/bgpcep/programming/impl/InstructionImpl.java
programming/impl/src/main/java/org/opendaylight/bgpcep/programming/impl/ProgrammingServiceImpl.java
programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedDataProviderWrapper.java [new file with mode: 0644]
programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedExecutorWrapper.java [new file with mode: 0644]
programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedNotificationServiceWrapper.java [new file with mode: 0644]
programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/ProgrammingServiceImplTest.java [new file with mode: 0644]

index 65fc2862be9b26c8ff71dd5fc3dcc7437cbdc0dd..4efc2b8a9d585e6f2750864a87c731cd689153de 100644 (file)
         </dependency>
 
         <!-- Testing dependencies -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>pcep-tunnel-api</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
                <dependency>
                        <groupId>${project.groupId}</groupId>
                        <artifactId>mockito-configuration</artifactId>
index e342405ee2fa252c54f7afc6091625ca636a35fa..3e0ab9a024219242c6f75cab1591443b307a0bf9 100644 (file)
@@ -7,14 +7,11 @@
  */
 package org.opendaylight.bgpcep.programming.impl;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 import io.netty.util.Timeout;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.annotation.concurrent.GuardedBy;
-
 import org.opendaylight.bgpcep.programming.spi.ExecutionResult;
 import org.opendaylight.bgpcep.programming.spi.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelFailure;
@@ -26,10 +23,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programm
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
+import javax.annotation.concurrent.GuardedBy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 final class InstructionImpl implements Instruction {
        private static final Logger LOG = LoggerFactory.getLogger(InstructionImpl.class);
@@ -218,6 +215,8 @@ final class InstructionImpl implements Instruction {
                        it.next().removeDependency(this);
                }
                dependants.clear();
+
+               this.queue.instructionRemoved();
        }
 
        synchronized ListenableFuture<ExecutionResult<Details>> ready() {
index de8bf65ccce4dd8da33a17932ce0ab17a16a1d01..634534eb36594ba8dad167c1603cc9a982eefce9 100644 (file)
@@ -7,20 +7,15 @@
  */
 package org.opendaylight.bgpcep.programming.impl;
 
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.SettableFuture;
 import io.netty.util.Timeout;
 import io.netty.util.Timer;
 import io.netty.util.TimerTask;
-
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
 import org.opendaylight.bgpcep.programming.NanotimeUtil;
 import org.opendaylight.bgpcep.programming.spi.ExecutionResult;
 import org.opendaylight.bgpcep.programming.spi.Instruction;
@@ -58,12 +53,15 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.SettableFuture;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 
 public final class ProgrammingServiceImpl implements AutoCloseable, InstructionScheduler, ProgrammingService {
        private static final Logger LOG = LoggerFactory.getLogger(ProgrammingServiceImpl.class);
@@ -162,7 +160,7 @@ public final class ProgrammingServiceImpl implements AutoCloseable, InstructionS
 
                for (final InstructionId id : input.getId()) {
                        // Find the instruction
-                       final InstructionImpl i = this.insns.get(input.getId());
+                       final InstructionImpl i = this.insns.get(id);
                        if (i == null) {
                                LOG.debug("Instruction {} not present in the graph", input.getId());
                                failed.add(id);
@@ -251,7 +249,7 @@ public final class ProgrammingServiceImpl implements AutoCloseable, InstructionS
                 *  and fail the operation.
                 */
                if (!unmet.isEmpty()) {
-                       throw new SchedulerException("Instruction's dependecies are already unsuccessful", new FailureBuilder().setType(DeadOnArrival.class).setFailedPreconditions(unmet).build());
+                       throw new SchedulerException("Instruction's dependencies are already unsuccessful", new FailureBuilder().setType(DeadOnArrival.class).setFailedPreconditions(unmet).build());
                }
 
                /*
diff --git a/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedDataProviderWrapper.java b/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedDataProviderWrapper.java
new file mode 100644 (file)
index 0000000..e07326a
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.programming.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.mockito.Matchers;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.Nanotime;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.InstructionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.Instructions;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.List;
+import java.util.concurrent.Future;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+final class MockedDataProviderWrapper {
+
+       private DataModificationTransaction lastMockedTransaction;
+       private DataProviderService lastMockedDataProvider;
+
+       private List<PutOperationalDataInvocationArgs> putOperationalDataInvocations;
+       private List<InstanceIdentifier<?>> removeOperationalDataInvocations;
+
+       MockedDataProviderWrapper() {
+               MockitoAnnotations.initMocks(this);
+               putOperationalDataInvocations = Lists.newArrayList();
+               removeOperationalDataInvocations = Lists.newArrayList();
+       }
+
+       public DataProviderService getMockedDataProvider() {
+               lastMockedTransaction = setupMockedTransaction();
+               lastMockedDataProvider = setupMockedDataProvider(lastMockedTransaction);
+               return lastMockedDataProvider;
+       }
+
+       private DataProviderService setupMockedDataProvider(DataModificationTransaction mockedTransaction) {
+               DataProviderService mockedDataProvider = mock(DataProviderService.class);
+               doReturn(mockedTransaction).when(mockedDataProvider).beginTransaction();
+
+               Future mockedCommitFuture = mock(Future.class);
+               doReturn(mockedCommitFuture).when(mockedTransaction).commit();
+               return mockedDataProvider;
+       }
+
+       private DataModificationTransaction setupMockedTransaction() {
+               DataModificationTransaction mockedTransaction = mock(DataModificationTransaction.class);
+
+               doReturn(null).when(mockedTransaction).readOperationalData(
+                               Matchers.<InstanceIdentifier<? extends DataObject>>any());
+
+               setPutAnswer(mockedTransaction);
+               setRemoveAnwer(mockedTransaction);
+               return mockedTransaction;
+       }
+
+       private void setRemoveAnwer(DataModificationTransaction mockedTransaction) {
+               doAnswer(new Answer<Void>() {
+                       @Override
+                       public Void answer(InvocationOnMock invocation) throws Throwable {
+                               removeOperationalDataInvocations.add((InstanceIdentifier<?>) invocation.getArguments()[0]);
+                               return null;
+                       }
+               }).when(mockedTransaction).removeOperationalData(
+                               Matchers.<InstanceIdentifier<? extends DataObject>> any());
+       }
+
+       private void setPutAnswer(DataModificationTransaction mockedTransaction) {
+               doAnswer(new Answer<Void>() {
+                       @Override
+                       public Void answer(InvocationOnMock invocation) throws Throwable {
+                               putOperationalDataInvocations.add(PutOperationalDataInvocationArgs.fromObjects(
+                                               invocation.getArguments()[0], invocation.getArguments()[1]));
+                               return null;
+                       }
+               }).when(mockedTransaction).putOperationalData(Matchers.<InstanceIdentifier<? extends DataObject>>any(),
+                               any(DataObject.class));
+       }
+
+       MockedDataProviderWrapper verifyBeginTransaction(int beginTransactionCount) {
+               verify(lastMockedDataProvider, times(beginTransactionCount)).beginTransaction();
+               return this;
+       }
+
+       MockedDataProviderWrapper verifyPutDataOnTransaction(int putCount) {
+               verify(lastMockedTransaction, times(putCount)).putOperationalData(Matchers.<InstanceIdentifier<? extends DataObject>>any(),
+                               any(DataObject.class));
+               return this;
+       }
+
+       MockedDataProviderWrapper verifyRemoveDataOnTransaction(int removeCount) {
+               verify(lastMockedTransaction, times(removeCount)).removeOperationalData(Matchers.<InstanceIdentifier<? extends DataObject>>any());
+               return this;
+       }
+
+       MockedDataProviderWrapper verifyCommitTransaction(int commitCount) {
+               verify(lastMockedTransaction, times(commitCount)).commit();
+               return this;
+       }
+
+       void assertPutDataForInstructions(int idx, InstructionId expectedId, Nanotime expectedDeadline) {
+               assertPutDataTargetType(idx, Instruction.class);
+               assertEquals(Instructions.class, putOperationalDataInvocations.get(idx).data.getImplementedInterface());
+               Instructions instructions = (Instructions) putOperationalDataInvocations.get(idx).data;
+               assertEquals(expectedId, instructions.getId());
+               if(expectedDeadline!=null)
+                       assertEquals(expectedDeadline, instructions.getDeadline());
+       }
+
+       public void assertPutDataTargetType(int idx, Class<?> targetType) {
+               assertEquals(targetType, putOperationalDataInvocations.get(idx).id.getTargetType());
+       }
+
+       public void assertRemoveDataForInstruction(int idx, InstructionId expectedId) {
+               assertEquals(Instruction.class, removeOperationalDataInvocations.get(idx).getTargetType());
+               InstanceIdentifier<? extends DataObject> instanceId = removeOperationalDataInvocations.get(idx)
+                               .firstIdentifierOf(Instruction.class);
+               assertNotNull(instanceId);
+
+               InstanceIdentifier.PathArgument instructionPathArg = instanceId.getPathArguments().get(1);
+               assertTrue(instructionPathArg instanceof InstanceIdentifier.IdentifiableItem);
+               InstructionKey expectedKey = new InstructionKey(expectedId);
+               assertEquals(expectedKey, ((InstanceIdentifier.IdentifiableItem<?, ?>) instructionPathArg).getKey());
+       }
+
+       static final class PutOperationalDataInvocationArgs {
+               private final InstanceIdentifier<?> id;
+               private final DataObject data;
+
+               private PutOperationalDataInvocationArgs(InstanceIdentifier<?> id, DataObject data) {
+                       this.id = id;
+                       this.data = data;
+               }
+
+               static PutOperationalDataInvocationArgs fromObjects(Object id, Object data) {
+                       Preconditions.checkArgument(id instanceof InstanceIdentifier<?>);
+                       Preconditions.checkArgument(data instanceof DataObject);
+                       return new PutOperationalDataInvocationArgs((InstanceIdentifier<?>)id, (DataObject)data);
+               }
+
+               @Override
+               public String toString() {
+                       final StringBuffer sb = new StringBuffer("PutOperationalDataInvocationArgs{");
+                       sb.append("id=").append(id);
+                       sb.append(", data=").append(data);
+                       sb.append('}');
+                       return sb.toString();
+               }
+       }
+}
diff --git a/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedExecutorWrapper.java b/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedExecutorWrapper.java
new file mode 100644 (file)
index 0000000..35bfa6b
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.programming.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+final class MockedExecutorWrapper {
+
+       private List<Object> submittedTasksToExecutor;
+
+       MockedExecutorWrapper() {
+               submittedTasksToExecutor = Lists.newArrayList();
+       }
+
+       ListeningExecutorService getMockedExecutor() {
+               ListeningExecutorService mockedExecutor = mock(ListeningExecutorService.class);
+               Answer<ListenableFuture<?>> submitAnswer = new Answer<ListenableFuture<?>>() {
+                       @Override
+                       public ListenableFuture<?> answer(InvocationOnMock invocation) throws Throwable {
+                               Object task = invocation.getArguments()[0];
+                               submittedTasksToExecutor.add(task);
+
+                               Object result = null;
+                               if(task instanceof Runnable) {
+                                       ((Runnable)task).run();
+                               } else if(task instanceof Callable) {
+                                       result = ((Callable<?>)task).call();
+                               }
+
+                               ListenableFuture<?> mockedFuture = mock(ListenableFuture.class);
+                               doReturn(result).when(mockedFuture).get();
+                               return mockedFuture;
+                       }
+               };
+               doAnswer(submitAnswer).when(mockedExecutor).submit(any(Runnable.class));
+               doAnswer(submitAnswer).when(mockedExecutor).submit(any(Callable.class));
+               return mockedExecutor;
+       }
+
+       void assertSubmittedTasksSize(int taskCount) {
+               assertEquals(taskCount, submittedTasksToExecutor.size());
+       }
+}
diff --git a/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedNotificationServiceWrapper.java b/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/MockedNotificationServiceWrapper.java
new file mode 100644 (file)
index 0000000..3051508
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * @author Maros Marsalek
+ *
+ * 01 2014
+ *
+ * Copyright (c) 2012 by Cisco Systems, Inc.
+ * All rights reserved.
+ */
+package org.opendaylight.bgpcep.programming.impl;
+
+import com.google.common.collect.Lists;
+import junit.framework.Assert;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionStatusChanged;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import java.util.List;
+
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+final class MockedNotificationServiceWrapper {
+       private List<Notification> publishedNotifications;
+
+       MockedNotificationServiceWrapper() {
+               publishedNotifications = Lists.newArrayList();
+       }
+
+       NotificationProviderService getMockedNotificationService() {
+               NotificationProviderService mockedNotificationService = mock(NotificationProviderService.class);
+
+               doAnswer(new Answer() {
+                       @Override
+                       public Object answer(InvocationOnMock invocation) throws Throwable {
+                               Object notif = invocation.getArguments()[0];
+                               assertTrue(Notification.class.isAssignableFrom(notif.getClass()));
+                               publishedNotifications.add((Notification) notif);
+                               return null;
+                       }
+               }).when(mockedNotificationService).publish(any(Notification.class));
+               return mockedNotificationService;
+       }
+
+       void assertNotificationsCount(int count) {
+               assertEquals(count, publishedNotifications.size());
+       }
+
+       public void assertInstructionStatusChangedNotification(int idx, InstructionId id, InstructionStatus status) {
+               assertTrue(InstructionStatusChanged.class.isAssignableFrom(publishedNotifications.get(idx).getClass()));
+               InstructionStatusChanged firstNotification = (InstructionStatusChanged) publishedNotifications.get(idx);
+               assertInstructionStatusChangedNotification(id, status, firstNotification);
+       }
+
+       private void assertInstructionStatusChangedNotification(InstructionId id, InstructionStatus status,
+                       InstructionStatusChanged firstNotification) {
+               Assert.assertEquals(id, firstNotification.getId());
+               Assert.assertEquals(status, firstNotification.getStatus());
+       }
+}
diff --git a/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/ProgrammingServiceImplTest.java b/programming/impl/src/test/java/org/opendaylight/bgpcep/programming/impl/ProgrammingServiceImplTest.java
new file mode 100644 (file)
index 0000000..731c62b
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.programming.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.opendaylight.bgpcep.programming.NanotimeUtil;
+import org.opendaylight.bgpcep.programming.spi.Instruction;
+import org.opendaylight.bgpcep.programming.spi.SchedulerException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelInstructionInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelInstructionInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionQueue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.Nanotime;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.SubmitInstructionInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.Details;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.DetailsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import java.math.BigInteger;
+import java.util.List;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class ProgrammingServiceImplTest {
+
+       public static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
+
+       private ProgrammingServiceImpl testedProgrammingService;
+       private MockedExecutorWrapper mockedExecutorWrapper;
+       private MockedDataProviderWrapper mockedDataProviderWrapper;
+       private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
+       private Timer timer = new HashedWheelTimer();
+
+       @Before
+       public void setUp() throws Exception {
+               mockedDataProviderWrapper = new MockedDataProviderWrapper();
+               mockedExecutorWrapper = new MockedExecutorWrapper();
+               mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
+
+               testedProgrammingService = new ProgrammingServiceImpl(mockedDataProviderWrapper.getMockedDataProvider(),
+                               mockedNotificationServiceWrapper.getMockedNotificationService(),
+                               mockedExecutorWrapper.getMockedExecutor(), timer);
+
+               mockedDataProviderWrapper.verifyBeginTransaction(1).verifyPutDataOnTransaction(1).verifyCommitTransaction(1);
+               mockedDataProviderWrapper.assertPutDataTargetType(0, InstructionQueue.class);
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void testScheduleInstruction() throws Exception {
+               SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
+               testedProgrammingService.scheduleInstruction(mockedSubmit);
+
+               // assert Schedule to executor
+               mockedExecutorWrapper.assertSubmittedTasksSize(1);
+
+               // assert Notification
+               mockedNotificationServiceWrapper.assertNotificationsCount(1);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(),
+                               InstructionStatus.Scheduled);
+
+               mockedDataProviderWrapper.verifyBeginTransaction(2).verifyPutDataOnTransaction(2).verifyCommitTransaction(2)
+                               .verifyRemoveDataOnTransaction(0);
+               mockedDataProviderWrapper.assertPutDataForInstructions(1, mockedSubmit.getId(), mockedSubmit.getDeadline());
+       }
+
+       @Test
+       public void testScheduleDependingInstruction() throws Exception {
+               testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
+               SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit2);
+
+               mockedExecutorWrapper.assertSubmittedTasksSize(2);
+
+               // First is in state scheduled, so second could not be scheduled yet
+               mockedNotificationServiceWrapper.assertNotificationsCount(1);
+       }
+
+       @Test
+       public void testScheduleDependingInstructionToFail() throws Exception {
+               try {
+                       testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit", "dep1"));
+               } catch (SchedulerException e) {
+                       assertThat(e.getMessage(), JUnitMatchers.containsString("Unknown dependency ID"));
+                       mockedNotificationServiceWrapper.assertNotificationsCount(0);
+                       return;
+               }
+               fail("Instruction schedule should fail on unresolved dependencies");
+       }
+
+       @Test
+       public void testCancelInstruction() throws Exception {
+               SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
+               testedProgrammingService.scheduleInstruction(mockedSubmit);
+
+               CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
+               testedProgrammingService.cancelInstruction(mockedCancel);
+
+               mockedExecutorWrapper.assertSubmittedTasksSize(2);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(2);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(),
+                               InstructionStatus.Cancelled);
+
+               mockedDataProviderWrapper.verifyBeginTransaction(2/*init + schedule first*/ + 1 /*cancel*/).
+                               verifyPutDataOnTransaction(3).verifyRemoveDataOnTransaction(0).verifyCommitTransaction(3);
+
+               mockedDataProviderWrapper.assertPutDataForInstructions(1, mockedSubmit.getId(), mockedSubmit.getDeadline());
+
+               mockedDataProviderWrapper.assertPutDataForInstructions(2, mockedCancel.getId(), null);
+       }
+
+       @Test
+       public void testCancelDependantInstruction() throws Exception {
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit1);
+               SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit2);
+               SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3", "mockedSubmit1",
+                               "mockedSubmit2");
+               testedProgrammingService.scheduleInstruction(mockedSubmit3);
+
+               testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/ + 3 /*First and all dependencies cancelled*/);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(),
+                               InstructionStatus.Scheduled);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
+                               InstructionStatus.Cancelled);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(),
+                               InstructionStatus.Cancelled);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(),
+                               InstructionStatus.Cancelled);
+       }
+
+       @Test
+       public void testCleanInstructions() throws Exception {
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit1);
+               SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit2);
+
+               CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
+               CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
+                               Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
+
+               ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = testedProgrammingService
+                               .cleanInstructions(cleanInstructionsInput);
+
+               assertCleanInstructionOutput(cleanedInstructionOutput, 2);
+
+               int expectedBeginTxCount = 1/*Service init*/ + 1;
+               mockedDataProviderWrapper.verifyBeginTransaction(expectedBeginTxCount/*Schedule first*/).
+                               verifyCommitTransaction(expectedBeginTxCount)
+                               .verifyPutDataOnTransaction(expectedBeginTxCount)
+                               .verifyRemoveDataOnTransaction(0);
+
+               testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
+
+               cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
+               assertCleanInstructionOutput(cleanedInstructionOutput, 0);
+               mockedDataProviderWrapper.verifyBeginTransaction(expectedBeginTxCount + 2/*Update status to cancelled*/ + 2 /*Cleanup*/)
+                               .verifyRemoveDataOnTransaction(2)
+                               .verifyPutDataOnTransaction(2/*From before*/ + 2/*Cancel*/)
+                               .verifyCommitTransaction(expectedBeginTxCount + 2/*Update status to cancelled*/ + 2 /*Cleanup*/);
+
+               mockedDataProviderWrapper.assertRemoveDataForInstruction(0, mockedSubmit1.getId());
+               mockedDataProviderWrapper.assertRemoveDataForInstruction(1, mockedSubmit2.getId());
+       }
+
+       private void assertCleanInstructionOutput(
+                       ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput, int unflushedCount)
+                       throws InterruptedException, java.util.concurrent.ExecutionException {
+               if(unflushedCount==0) {
+                       Assert.assertNull(cleanedInstructionOutput.get().getResult().getUnflushed());
+               } else {
+                       Assert.assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
+               }
+               Assert.assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
+       }
+
+       @Test
+       public void testCloseProgrammingService() throws Exception {
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit1);
+               SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit2);
+
+               testedProgrammingService.close();
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(1/* First scheduled */ + 2/* Both cancelled at close */);
+               mockedDataProviderWrapper.verifyRemoveDataOnTransaction(1);
+       }
+
+       @Test(timeout = 30 * 1000)
+       public void testTimeoutWhileScheduledTransaction() throws Exception {
+               BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
+               Nanotime current = NanotimeUtil.currentTime();
+               Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
+
+               Optional<Nanotime> deadline = Optional.of(deadlineNano);
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
+               ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(1);
+
+               future.get();
+
+               Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(2);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
+                               InstructionStatus.Cancelled);
+       }
+
+       @Test(timeout = 30 * 1000)
+       public void testTimeoutWhileSuccessfulTransaction() throws Exception {
+               BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
+               Nanotime current = NanotimeUtil.currentTime();
+               Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
+
+               Optional<Nanotime> deadline = Optional.of(deadlineNano);
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
+               ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(1);
+
+               Instruction i = future.get();
+               i.checkedExecutionStart();
+               i.executionCompleted(InstructionStatus.Successful, getDetails());
+
+               Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(3);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
+                               InstructionStatus.Executing);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
+                               InstructionStatus.Successful);
+               // Timeout in success should not do anything
+       }
+
+       @Test(timeout = 30 * 1000)
+       public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
+               BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
+               Nanotime current = NanotimeUtil.currentTime();
+               Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
+
+               Optional<Nanotime> deadline = Optional.of(deadlineNano);
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
+               ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
+
+               SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
+               testedProgrammingService.scheduleInstruction(mockedSubmit2);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(1);
+
+               Instruction i = future.get();
+               i.checkedExecutionStart();
+
+               Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(4);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
+                               InstructionStatus.Executing);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
+                               InstructionStatus.Unknown);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
+                               InstructionStatus.Cancelled);
+       }
+
+       // TODO test deadline with state Queued
+
+       @Test
+       public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
+               SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
+               ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
+
+               SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
+               ListenableFuture<Instruction> future2 = testedProgrammingService.scheduleInstruction(mockedSubmit2);
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(1);
+
+               Instruction i = future.get();
+               i.checkedExecutionStart();
+               i.executionCompleted(InstructionStatus.Successful, getDetails());
+
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(4);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
+                               InstructionStatus.Executing);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
+                               InstructionStatus.Successful);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
+                               InstructionStatus.Scheduled);
+
+               i = future2.get();
+               i.checkedExecutionStart();
+               i.executionCompleted(InstructionStatus.Successful, getDetails());
+
+               mockedNotificationServiceWrapper.assertNotificationsCount(6);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(),
+                               InstructionStatus.Executing);
+               mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(),
+                               InstructionStatus.Successful);
+       }
+
+       private Details getDetails() {
+               return new DetailsBuilder().build();
+       }
+
+       private SubmitInstructionInput getMockedSubmitInstructionInput(String id, String... dependencyIds) {
+               return getMockedSubmitInstructionInput(id, Optional.<Nanotime>absent(), dependencyIds);
+       }
+
+       private SubmitInstructionInput getMockedSubmitInstructionInput(String id, Optional<Nanotime> deadline, String... dependencyIds) {
+               SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
+
+               doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
+               List<InstructionId> dependencies = Lists.newArrayList();
+               for (String dependencyId : dependencyIds) {
+                       dependencies.add(getInstructionId(dependencyId));
+               }
+
+               doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
+               doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
+               doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))).when(
+                               mockedSubmitInstruction).getDeadline();
+               return mockedSubmitInstruction;
+       }
+
+       private CancelInstructionInput getCancelInstruction(String instructionId) {
+               CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
+               builder.setId(getInstructionId(instructionId));
+               return builder.build();
+       }
+
+       private InstructionId getInstructionId(String id) {
+               return new InstructionId(id);
+       }
+
+}