Code clean up
[bgpcep.git] / programming / impl / src / test / java / org / opendaylight / bgpcep / programming / impl / ProgrammingServiceImplTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.bgpcep.programming.impl;
9
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertThat;
13 import static org.junit.Assert.assertTrue;
14 import static org.junit.Assert.fail;
15 import static org.mockito.Mockito.doReturn;
16 import static org.mockito.Mockito.mock;
17 import static org.opendaylight.protocol.util.CheckUtil.checkNotPresentOperational;
18 import static org.opendaylight.protocol.util.CheckUtil.checkPresentOperational;
19
20 import com.google.common.collect.Lists;
21 import com.google.common.util.concurrent.ListenableFuture;
22 import io.netty.util.HashedWheelTimer;
23 import io.netty.util.Timer;
24 import java.math.BigInteger;
25 import java.util.List;
26 import java.util.Optional;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.opendaylight.bgpcep.programming.NanotimeUtil;
31 import org.opendaylight.bgpcep.programming.spi.Instruction;
32 import org.opendaylight.bgpcep.programming.spi.SchedulerException;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInputBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueue;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueueKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.Nanotime;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.SubmitInstructionInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.InstructionKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51
52 public class ProgrammingServiceImplTest extends AbstractProgrammingTest {
53
54     private static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
55     private static final String INSTRUCTIONS_QUEUE_KEY = "test-instraction-queue";
56     private final Timer timer = new HashedWheelTimer();
57     private MockedExecutorWrapper mockedExecutorWrapper;
58     private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
59     private ProgrammingServiceImpl testedProgrammingService;
60
61     @Before
62     @Override
63     public void setUp() throws Exception {
64         super.setUp();
65         this.mockedExecutorWrapper = new MockedExecutorWrapper();
66         this.mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
67
68         this.testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(),
69                 this.mockedNotificationServiceWrapper.getMockedNotificationService(),
70                 this.mockedExecutorWrapper.getMockedExecutor(), this.rpcRegistry, this.cssp, this.timer,
71                 INSTRUCTIONS_QUEUE_KEY);
72         this.singletonService.instantiateServiceInstance();
73     }
74
75     @After
76     public void tearDown() throws Exception {
77         this.singletonService.closeServiceInstance();
78         this.testedProgrammingService.close();
79     }
80
81     @Test
82     public void testScheduleInstruction() throws Exception {
83         final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
84         this.testedProgrammingService.scheduleInstruction(mockedSubmit);
85
86         checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
87
88         // assert Schedule to executor
89         this.mockedExecutorWrapper.assertSubmittedTasksSize(1);
90
91         // assert Notification
92         this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
93         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(),
94                 InstructionStatus.Scheduled);
95     }
96
97     @Test
98     public void testScheduleDependingInstruction() throws Exception {
99         this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
100         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
101                 "mockedSubmit1");
102         this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
103
104         this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
105
106         // First is in state scheduled, so second could not be scheduled yet
107         this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
108     }
109
110     @Test
111     public void testScheduleDependingInstructionToFail() throws Exception {
112         try {
113             this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit",
114                     "dep1"));
115         } catch (final SchedulerException e) {
116             assertThat(e.getMessage(), containsString("Unknown dependency ID"));
117             this.mockedNotificationServiceWrapper.assertNotificationsCount(0);
118             return;
119         }
120         fail("Instruction schedule should fail on unresolved dependencies");
121     }
122
123     @Test
124     public void testCancelInstruction() throws Exception {
125         final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
126         this.testedProgrammingService.scheduleInstruction(mockedSubmit);
127         checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
128
129         final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
130         this.testedProgrammingService.cancelInstruction(mockedCancel);
131
132         checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
133         this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
134
135         this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
136         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(),
137                 InstructionStatus.Cancelled);
138     }
139
140     @Test
141     public void testCancelDependantInstruction() throws Exception {
142         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
143         this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
144         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
145                 "mockedSubmit1");
146         this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
147         final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3",
148                 "mockedSubmit1", "mockedSubmit2");
149         this.testedProgrammingService.scheduleInstruction(mockedSubmit3);
150
151         this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
152
153         this.mockedNotificationServiceWrapper
154                 .assertNotificationsCount(1 /*First Scheduled*/ + 3 /*First and all dependencies cancelled*/);
155         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(),
156                 InstructionStatus.Scheduled);
157         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
158                 InstructionStatus.Cancelled);
159         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(),
160                 InstructionStatus.Cancelled);
161         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(),
162                 InstructionStatus.Cancelled);
163
164         checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
165         checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
166         checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit3.getId()));
167     }
168
169     @Test
170     public void testCleanInstructions() throws Exception {
171         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
172         this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
173         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
174                 "mockedSubmit1");
175         this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
176
177         final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
178         final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
179                 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
180
181         ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = this.testedProgrammingService
182                 .cleanInstructions(cleanInstructionsInput);
183
184         assertCleanInstructionOutput(cleanedInstructionOutput, 2);
185
186         this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
187
188         cleanedInstructionOutput = this.testedProgrammingService.cleanInstructions(cleanInstructionsInput);
189         assertCleanInstructionOutput(cleanedInstructionOutput, 0);
190
191         checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
192         checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
193     }
194
195     private static void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>>
196             cleanedInstructionOutput, final int unflushedCount) throws InterruptedException,
197             java.util.concurrent.ExecutionException {
198         if (unflushedCount == 0) {
199             final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
200             assertTrue(unflushed == null || unflushed.isEmpty());
201         } else {
202             assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
203         }
204         assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
205     }
206
207     @Test
208     public void testCloseProgrammingService() throws Exception {
209         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
210         this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
211         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
212                 "mockedSubmit1");
213         this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
214
215         this.testedProgrammingService.close();
216
217         this.mockedNotificationServiceWrapper
218                 .assertNotificationsCount(1/* First scheduled */ + 2/* Both cancelled at close */);
219     }
220
221     @Test(timeout = 30 * 1000)
222     public void testTimeoutWhileScheduledTransaction() throws Exception {
223         final BigInteger deadlineOffset = BigInteger.valueOf(
224                 1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
225         final Nanotime current = NanotimeUtil.currentTime();
226         final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
227
228         final Optional<Nanotime> deadline = Optional.of(deadlineNano);
229         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
230         final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
231
232         this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
233
234         future.get();
235
236         this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
237         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
238                 InstructionStatus.Cancelled);
239     }
240
241     @Test(timeout = 30 * 1000)
242     public void testTimeoutWhileSuccessfulTransaction() throws Exception {
243         final BigInteger deadlineOffset = BigInteger.valueOf(
244                 1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
245         final Nanotime current = NanotimeUtil.currentTime();
246         final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
247
248         final Optional<Nanotime> deadline = Optional.of(deadlineNano);
249         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
250         final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
251
252         this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
253
254         final Instruction i = future.get();
255         i.checkedExecutionStart();
256         i.executionCompleted(InstructionStatus.Successful, getDetails());
257
258         this.mockedNotificationServiceWrapper.assertNotificationsCount(3);
259         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
260                 InstructionStatus.Executing);
261         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
262                 InstructionStatus.Successful);
263         // Timeout in success should not do anything
264     }
265
266     @Test(timeout = 30 * 1000)
267     public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
268         final BigInteger deadlineOffset = BigInteger.valueOf(
269                 1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
270         final Nanotime current = NanotimeUtil.currentTime();
271         final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
272
273         final Optional<Nanotime> deadline = Optional.of(deadlineNano);
274         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
275         final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
276
277         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
278                 "mockedSubmit1");
279         this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
280
281         this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
282
283         final Instruction i = future.get();
284         i.checkedExecutionStart();
285
286         this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
287         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
288                 InstructionStatus.Executing);
289         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
290                 InstructionStatus.Unknown);
291         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
292                 InstructionStatus.Cancelled);
293     }
294
295     // TODO test deadline with state Queued
296
297     @Test
298     public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
299         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
300         final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
301
302         final SubmitInstructionInput mockedSubmit2 =
303                 getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
304         final ListenableFuture<Instruction> future2 = this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
305
306         this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
307
308         Instruction instruction = future.get();
309         instruction.checkedExecutionStart();
310         instruction.executionCompleted(InstructionStatus.Successful, getDetails());
311
312         this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
313         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
314                 InstructionStatus.Executing);
315         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
316                 InstructionStatus.Successful);
317         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
318                 InstructionStatus.Scheduled);
319
320         instruction = future2.get();
321         instruction.checkedExecutionStart();
322         instruction.executionCompleted(InstructionStatus.Successful, getDetails());
323
324         this.mockedNotificationServiceWrapper.assertNotificationsCount(6);
325         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(),
326                 InstructionStatus.Executing);
327         this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(),
328                 InstructionStatus.Successful);
329     }
330
331     private static Details getDetails() {
332         return new DetailsBuilder().build();
333     }
334
335     private static SubmitInstructionInput getMockedSubmitInstructionInput(final String id,
336             final String... dependencyIds) {
337         return getMockedSubmitInstructionInput(id, Optional.empty(), dependencyIds);
338     }
339
340     private static SubmitInstructionInput getMockedSubmitInstructionInput(final String id,
341             final Optional<Nanotime> deadline, final String... dependencyIds) {
342         final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
343
344         doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
345         final List<InstructionId> dependencies = Lists.newArrayList();
346         for (final String dependencyId : dependencyIds) {
347             dependencies.add(new InstructionId(dependencyId));
348         }
349
350         doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
351         doReturn(new InstructionId(id)).when(mockedSubmitInstruction).getId();
352         doReturn(deadline.orElseGet(() -> new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))))
353                 .when(mockedSubmitInstruction).getDeadline();
354         return mockedSubmitInstruction;
355     }
356
357     private static CancelInstructionInput getCancelInstruction(final String instructionId) {
358         final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
359         builder.setId(new InstructionId(instructionId));
360         return builder.build();
361     }
362
363     private static KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming
364             .rev150720.instruction.queue.Instruction, InstructionKey> buildInstructionIID(final InstructionId id) {
365         return InstanceIdentifier.builder(InstructionsQueue.class, new InstructionsQueueKey(INSTRUCTIONS_QUEUE_KEY))
366                 .build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720
367                         .instruction.queue.Instruction.class, new InstructionKey(id));
368     }
369 }