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