Merge "Bug 2273: Removed unbuilt third-party code."
[controller.git] / opendaylight / md-sal / topology-manager / src / test / java / org / opendaylight / md / controller / topology / manager / FlowCapableTopologyExporterTest.java
1 /*
2  * Copyright (c) 2014 Brocade Communications 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
9 package org.opendaylight.md.controller.topology.manager;
10
11 import com.google.common.base.Optional;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.SettableFuture;
15 import com.google.common.util.concurrent.Uninterruptibles;
16 import org.junit.After;
17 import org.junit.Before;
18 import org.junit.Test;
19 import org.mockito.ArgumentCaptor;
20 import org.mockito.InOrder;
21 import org.mockito.Mock;
22 import org.mockito.MockitoAnnotations;
23 import org.mockito.invocation.InvocationOnMock;
24 import org.mockito.stubbing.Answer;
25 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdatedBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdatedBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkDiscoveredBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkRemovedBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.StateBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemovedBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdatedBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemovedBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdatedBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.topology.inventory.rev131030.InventoryNode;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.topology.inventory.rev131030.InventoryNodeConnector;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
67 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
68 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
69 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
70 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
73
74 import java.util.Arrays;
75 import java.util.HashSet;
76 import java.util.List;
77 import java.util.Set;
78 import java.util.concurrent.CountDownLatch;
79 import java.util.concurrent.ExecutorService;
80 import java.util.concurrent.Executors;
81 import java.util.concurrent.TimeUnit;
82
83 import static org.junit.Assert.assertEquals;
84 import static org.junit.Assert.assertNotNull;
85 import static org.junit.Assert.assertTrue;
86 import static org.junit.Assert.fail;
87 import static org.mockito.Mockito.any;
88 import static org.mockito.Mockito.atLeast;
89 import static org.mockito.Mockito.doAnswer;
90 import static org.mockito.Mockito.doReturn;
91 import static org.mockito.Mockito.eq;
92 import static org.mockito.Mockito.inOrder;
93 import static org.mockito.Mockito.mock;
94 import static org.mockito.Mockito.never;
95 import static org.mockito.Mockito.verify;
96
97 public class FlowCapableTopologyExporterTest {
98
99     @Mock
100     private DataBroker mockDataBroker;
101
102     @Mock
103     private BindingTransactionChain mockTxChain;
104
105     private OperationProcessor processor;
106
107     private FlowCapableTopologyExporter exporter;
108
109     private InstanceIdentifier<Topology> topologyIID;
110
111     private final ExecutorService executor = Executors.newFixedThreadPool(1);
112
113     @Before
114     public void setUp() {
115         MockitoAnnotations.initMocks(this);
116
117         doReturn(mockTxChain).when(mockDataBroker)
118                 .createTransactionChain(any(TransactionChainListener.class));
119
120         processor = new OperationProcessor(mockDataBroker);
121
122         topologyIID = InstanceIdentifier.create(NetworkTopology.class)
123                 .child(Topology.class, new TopologyKey(new TopologyId("test")));
124         exporter = new FlowCapableTopologyExporter(processor, topologyIID);
125
126         executor.execute(processor);
127     }
128
129     @After
130     public void tearDown() {
131         executor.shutdownNow();
132     }
133
134     @SuppressWarnings({ "rawtypes" })
135     @Test
136     public void testOnNodeRemoved() {
137
138         NodeKey topoNodeKey = new NodeKey(new NodeId("node1"));
139         InstanceIdentifier<Node> topoNodeII = topologyIID.child(Node.class, topoNodeKey);
140         Node topoNode = new NodeBuilder().setKey(topoNodeKey).build();
141
142         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
143                                                                 nodeKey = newInvNodeKey(topoNodeKey.getNodeId().getValue());
144         InstanceIdentifier<?> invNodeID = InstanceIdentifier.create(Nodes.class).child(
145                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
146                 nodeKey);
147
148         List<Link> linkList = Arrays.asList(
149                 newLink("link1", newSourceNode("node1"), newDestNode("dest")),
150                 newLink("link2", newSourceNode("source"), newDestNode("node1")),
151                 newLink("link2", newSourceNode("source2"), newDestNode("dest2")));
152         final Topology topology = new TopologyBuilder().setLink(linkList).build();
153
154         InstanceIdentifier[] expDeletedIIDs = {
155                 topologyIID.child(Link.class, linkList.get(0).getKey()),
156                 topologyIID.child(Link.class, linkList.get(1).getKey()),
157                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
158             };
159
160         SettableFuture<Optional<Topology>> readFuture = SettableFuture.create();
161         readFuture.set(Optional.of(topology));
162         ReadWriteTransaction mockTx1 = mock(ReadWriteTransaction.class);
163         doReturn(Futures.makeChecked(readFuture, ReadFailedException.MAPPER)).when(mockTx1)
164                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
165
166         SettableFuture<Optional<Node>> readFutureNode = SettableFuture.create();
167         readFutureNode.set(Optional.of(topoNode));
168         doReturn(Futures.makeChecked(readFutureNode, ReadFailedException.MAPPER)).when(mockTx1)
169                 .read(LogicalDatastoreType.OPERATIONAL, topoNodeII);
170
171         CountDownLatch submitLatch1 = setupStubbedSubmit(mockTx1);
172
173         int expDeleteCalls = expDeletedIIDs.length;
174         CountDownLatch deleteLatch = new CountDownLatch(expDeleteCalls);
175         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
176                 ArgumentCaptor.forClass(InstanceIdentifier.class);
177         setupStubbedDeletes(mockTx1, deletedLinkIDs, deleteLatch);
178
179         doReturn(mockTx1).when(mockTxChain).newReadWriteTransaction();
180
181         exporter.onNodeRemoved(new NodeRemovedBuilder().setNodeRef(new NodeRef(invNodeID)).build());
182
183         waitForSubmit(submitLatch1);
184
185         setReadFutureAsync(topology, readFuture);
186
187         waitForDeletes(expDeleteCalls, deleteLatch);
188
189         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
190
191         verifyMockTx(mockTx1);
192     }
193
194     @SuppressWarnings({ "rawtypes" })
195     @Test
196     public void testOnNodeRemovedWithNoTopology() {
197
198         NodeKey topoNodeKey = new NodeKey(new NodeId("node1"));
199         InstanceIdentifier<Node> topoNodeII = topologyIID.child(Node.class, topoNodeKey);
200         Node topoNode = new NodeBuilder().setKey(topoNodeKey).build();
201
202         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
203                 nodeKey = newInvNodeKey(topoNodeKey.getNodeId().getValue());
204         InstanceIdentifier<?> invNodeID = InstanceIdentifier.create(Nodes.class).child(
205                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
206                 nodeKey);
207
208         InstanceIdentifier[] expDeletedIIDs = {
209                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
210             };
211
212         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
213         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockTx)
214                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
215         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
216
217         SettableFuture<Optional<Node>> readFutureNode = SettableFuture.create();
218         readFutureNode.set(Optional.of(topoNode));
219         doReturn(Futures.makeChecked(readFutureNode, ReadFailedException.MAPPER)).when(mockTx)
220                 .read(LogicalDatastoreType.OPERATIONAL, topoNodeII);
221
222         CountDownLatch deleteLatch = new CountDownLatch(1);
223         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
224                 ArgumentCaptor.forClass(InstanceIdentifier.class);
225         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
226
227         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
228
229         exporter.onNodeRemoved(new NodeRemovedBuilder().setNodeRef(new NodeRef(invNodeID)).build());
230
231         waitForSubmit(submitLatch);
232
233         waitForDeletes(1, deleteLatch);
234
235         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
236     }
237
238     @SuppressWarnings("rawtypes")
239     @Test
240     public void testOnNodeConnectorRemoved() {
241
242         NodeKey topoNodeKey = new NodeKey(new NodeId("node1"));
243         TerminationPointKey terminationPointKey = new TerminationPointKey(new TpId("tp1"));
244
245         InstanceIdentifier<Node> topoNodeII = topologyIID.child(Node.class, topoNodeKey);
246         Node topoNode = new NodeBuilder().setKey(topoNodeKey).build();
247
248         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
249                                                                   nodeKey = newInvNodeKey(topoNodeKey.getNodeId().getValue());
250
251         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
252                 newInvNodeConnKey(terminationPointKey.getTpId().getValue());
253
254         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
255
256         List<Link> linkList = Arrays.asList(
257                 newLink("link1", newSourceTp("tp1"), newDestTp("dest")),
258                 newLink("link2", newSourceTp("source"), newDestTp("tp1")),
259                 newLink("link3", newSourceTp("source2"), newDestTp("dest2")));
260         final Topology topology = new TopologyBuilder().setLink(linkList).build();
261
262         InstanceIdentifier[] expDeletedIIDs = {
263                 topologyIID.child(Link.class, linkList.get(0).getKey()),
264                 topologyIID.child(Link.class, linkList.get(1).getKey()),
265                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
266                         .child(TerminationPoint.class, new TerminationPointKey(new TpId("tp1")))
267             };
268
269         final SettableFuture<Optional<Topology>> readFuture = SettableFuture.create();
270         readFuture.set(Optional.of(topology));
271         ReadWriteTransaction mockTx1 = mock(ReadWriteTransaction.class);
272         doReturn(Futures.makeChecked(readFuture, ReadFailedException.MAPPER)).when(mockTx1)
273                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
274
275         SettableFuture<Optional<Node>> readFutureNode = SettableFuture.create();
276         readFutureNode.set(Optional.of(topoNode));
277         doReturn(Futures.makeChecked(readFutureNode, ReadFailedException.MAPPER)).when(mockTx1)
278                 .read(LogicalDatastoreType.OPERATIONAL, topoNodeII);
279
280         CountDownLatch submitLatch1 = setupStubbedSubmit(mockTx1);
281
282         int expDeleteCalls = expDeletedIIDs.length;
283         CountDownLatch deleteLatch = new CountDownLatch(expDeleteCalls);
284         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
285                 ArgumentCaptor.forClass(InstanceIdentifier.class);
286         setupStubbedDeletes(mockTx1, deletedLinkIDs, deleteLatch);
287
288         doReturn(mockTx1).when(mockTxChain).newReadWriteTransaction();
289
290         exporter.onNodeConnectorRemoved(new NodeConnectorRemovedBuilder().setNodeConnectorRef(
291                 new NodeConnectorRef(invNodeConnID)).build());
292
293         waitForSubmit(submitLatch1);
294
295         setReadFutureAsync(topology, readFuture);
296
297         waitForDeletes(expDeleteCalls, deleteLatch);
298
299         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
300
301         verifyMockTx(mockTx1);
302     }
303
304     @SuppressWarnings("rawtypes")
305     @Test
306     public void testOnNodeConnectorRemovedWithNoTopology() {
307
308         NodeKey topoNodeKey = new NodeKey(new NodeId("node1"));
309         TerminationPointKey terminationPointKey = new TerminationPointKey(new TpId("tp1"));
310
311         InstanceIdentifier<Node> topoNodeII = topologyIID.child(Node.class, topoNodeKey);
312         Node topoNode = new NodeBuilder().setKey(topoNodeKey).build();
313
314         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
315                 nodeKey = newInvNodeKey(topoNodeKey.getNodeId().getValue());
316
317         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
318                 newInvNodeConnKey(terminationPointKey.getTpId().getValue());
319
320         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
321
322         InstanceIdentifier[] expDeletedIIDs = {
323                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
324                         .child(TerminationPoint.class, new TerminationPointKey(new TpId("tp1")))
325             };
326
327         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
328         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockTx)
329                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
330         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
331
332         SettableFuture<Optional<Node>> readFutureNode = SettableFuture.create();
333         readFutureNode.set(Optional.of(topoNode));
334         doReturn(Futures.makeChecked(readFutureNode, ReadFailedException.MAPPER)).when(mockTx)
335                 .read(LogicalDatastoreType.OPERATIONAL, topoNodeII);
336
337         CountDownLatch deleteLatch = new CountDownLatch(1);
338         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
339                 ArgumentCaptor.forClass(InstanceIdentifier.class);
340         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
341
342         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
343
344         exporter.onNodeConnectorRemoved(new NodeConnectorRemovedBuilder().setNodeConnectorRef(
345                 new NodeConnectorRef(invNodeConnID)).build());
346
347         waitForSubmit(submitLatch);
348
349         waitForDeletes(1, deleteLatch);
350
351         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
352     }
353
354     @Test
355     public void testOnNodeUpdated() {
356
357         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
358                                                             nodeKey = newInvNodeKey("node1");
359         InstanceIdentifier<?> invNodeID = InstanceIdentifier.create(Nodes.class).child(
360                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
361                 nodeKey);
362
363         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
364         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
365         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
366
367         exporter.onNodeUpdated(new NodeUpdatedBuilder().setNodeRef(new NodeRef(invNodeID))
368                 .setId(nodeKey.getId()).addAugmentation(FlowCapableNodeUpdated.class,
369                         new FlowCapableNodeUpdatedBuilder().build()).build());
370
371         waitForSubmit(submitLatch);
372
373         ArgumentCaptor<Node> mergedNode = ArgumentCaptor.forClass(Node.class);
374         NodeId expNodeId = new NodeId("node1");
375         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(topologyIID.child(Node.class,
376                 new NodeKey(expNodeId))), mergedNode.capture(), eq(true));
377         assertEquals("getNodeId", expNodeId, mergedNode.getValue().getNodeId());
378         InventoryNode augmentation = mergedNode.getValue().getAugmentation(InventoryNode.class);
379         assertNotNull("Missing augmentation", augmentation);
380         assertEquals("getInventoryNodeRef", new NodeRef(invNodeID), augmentation.getInventoryNodeRef());
381     }
382
383     @SuppressWarnings("rawtypes")
384     @Test
385     public void testOnNodeConnectorUpdated() {
386
387         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
388                                                                  nodeKey = newInvNodeKey("node1");
389
390         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
391                 newInvNodeConnKey("tp1");
392
393         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
394
395         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
396         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
397         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
398
399         exporter.onNodeConnectorUpdated(new NodeConnectorUpdatedBuilder().setNodeConnectorRef(
400                 new NodeConnectorRef(invNodeConnID)).setId(ncKey.getId()).addAugmentation(
401                         FlowCapableNodeConnectorUpdated.class,
402                         new FlowCapableNodeConnectorUpdatedBuilder().build()).build());
403
404         waitForSubmit(submitLatch);
405
406         ArgumentCaptor<TerminationPoint> mergedNode = ArgumentCaptor.forClass(TerminationPoint.class);
407         NodeId expNodeId = new NodeId("node1");
408         TpId expTpId = new TpId("tp1");
409         InstanceIdentifier<TerminationPoint> expTpPath = topologyIID.child(
410                 Node.class, new NodeKey(expNodeId)).child(TerminationPoint.class,
411                         new TerminationPointKey(expTpId));
412         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(expTpPath),
413                 mergedNode.capture(), eq(true));
414         assertEquals("getTpId", expTpId, mergedNode.getValue().getTpId());
415         InventoryNodeConnector augmentation = mergedNode.getValue().getAugmentation(
416                 InventoryNodeConnector.class);
417         assertNotNull("Missing augmentation", augmentation);
418         assertEquals("getInventoryNodeConnectorRef", new NodeConnectorRef(invNodeConnID),
419                 augmentation.getInventoryNodeConnectorRef());
420     }
421
422     @SuppressWarnings("rawtypes")
423     @Test
424     public void testOnNodeConnectorUpdatedWithLinkStateDown() {
425
426         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
427                                                                  nodeKey = newInvNodeKey("node1");
428
429         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
430                 newInvNodeConnKey("tp1");
431
432         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
433
434         List<Link> linkList = Arrays.asList(newLink("link1", newSourceTp("tp1"), newDestTp("dest")));
435         Topology topology = new TopologyBuilder().setLink(linkList).build();
436
437         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
438         doReturn(Futures.immediateCheckedFuture(Optional.of(topology))).when(mockTx)
439                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
440         setupStubbedSubmit(mockTx);
441
442         CountDownLatch deleteLatch = new CountDownLatch(1);
443         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
444                 ArgumentCaptor.forClass(InstanceIdentifier.class);
445         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
446
447         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
448
449         exporter.onNodeConnectorUpdated(new NodeConnectorUpdatedBuilder().setNodeConnectorRef(
450                 new NodeConnectorRef(invNodeConnID)).setId(ncKey.getId()).addAugmentation(
451                         FlowCapableNodeConnectorUpdated.class,
452                         new FlowCapableNodeConnectorUpdatedBuilder().setState(
453                                 new StateBuilder().setLinkDown(true).build()).build()).build());
454
455         waitForDeletes(1, deleteLatch);
456
457         InstanceIdentifier<TerminationPoint> expTpPath = topologyIID.child(
458                 Node.class, new NodeKey(new NodeId("node1"))).child(TerminationPoint.class,
459                         new TerminationPointKey(new TpId("tp1")));
460
461         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(expTpPath),
462                 any(TerminationPoint.class), eq(true));
463
464         assertDeletedIDs(new InstanceIdentifier[]{topologyIID.child(Link.class,
465                 linkList.get(0).getKey())}, deletedLinkIDs);
466     }
467
468
469     @SuppressWarnings("rawtypes")
470     @Test
471     public void testOnNodeConnectorUpdatedWithPortDown() {
472
473         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
474                                                                  nodeKey = newInvNodeKey("node1");
475
476         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
477                 newInvNodeConnKey("tp1");
478
479         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
480
481         List<Link> linkList = Arrays.asList(newLink("link1", newSourceTp("tp1"), newDestTp("dest")));
482         Topology topology = new TopologyBuilder().setLink(linkList).build();
483
484         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
485         doReturn(Futures.immediateCheckedFuture(Optional.of(topology))).when(mockTx)
486                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
487         setupStubbedSubmit(mockTx);
488
489         CountDownLatch deleteLatch = new CountDownLatch(1);
490         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
491                 ArgumentCaptor.forClass(InstanceIdentifier.class);
492         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
493
494         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
495
496         exporter.onNodeConnectorUpdated(new NodeConnectorUpdatedBuilder().setNodeConnectorRef(
497                 new NodeConnectorRef(invNodeConnID)).setId(ncKey.getId()).addAugmentation(
498                         FlowCapableNodeConnectorUpdated.class,
499                         new FlowCapableNodeConnectorUpdatedBuilder().setConfiguration(
500                                 new PortConfig(true, true, true, true)).build()).build());
501
502         waitForDeletes(1, deleteLatch);
503
504         InstanceIdentifier<TerminationPoint> expTpPath = topologyIID.child(
505                 Node.class, new NodeKey(new NodeId("node1"))).child(TerminationPoint.class,
506                         new TerminationPointKey(new TpId("tp1")));
507
508         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(expTpPath),
509                 any(TerminationPoint.class), eq(true));
510
511         assertDeletedIDs(new InstanceIdentifier[]{topologyIID.child(Link.class,
512                 linkList.get(0).getKey())}, deletedLinkIDs);
513     }
514
515     @Test
516     public void testOnLinkDiscovered() {
517
518         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
519                 sourceNodeKey = newInvNodeKey("sourceNode");
520         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
521                 sourceNodeConnKey = newInvNodeConnKey("sourceTP");
522         InstanceIdentifier<?> sourceConnID = newNodeConnID(sourceNodeKey, sourceNodeConnKey);
523
524         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
525                 destNodeKey = newInvNodeKey("destNode");
526         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
527                 destNodeConnKey = newInvNodeConnKey("destTP");
528         InstanceIdentifier<?> destConnID = newNodeConnID(destNodeKey, destNodeConnKey);
529
530         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
531         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
532         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
533
534         exporter.onLinkDiscovered(new LinkDiscoveredBuilder().setSource(
535                 new NodeConnectorRef(sourceConnID)).setDestination(
536                         new NodeConnectorRef(destConnID)).build());
537
538         waitForSubmit(submitLatch);
539
540         ArgumentCaptor<Link> mergedNode = ArgumentCaptor.forClass(Link.class);
541         verify(mockTx).put(eq(LogicalDatastoreType.OPERATIONAL), eq(topologyIID.child(
542                         Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId())))),
543                 mergedNode.capture(), eq(true));
544         assertEquals("Source node ID", "sourceNode",
545                 mergedNode.getValue().getSource().getSourceNode().getValue());
546         assertEquals("Dest TP ID", "sourceTP",
547                 mergedNode.getValue().getSource().getSourceTp().getValue());
548         assertEquals("Dest node ID", "destNode",
549                 mergedNode.getValue().getDestination().getDestNode().getValue());
550         assertEquals("Dest TP ID", "destTP",
551                 mergedNode.getValue().getDestination().getDestTp().getValue());
552     }
553
554     @Test
555     public void testOnLinkRemoved() {
556
557         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
558                 sourceNodeKey = newInvNodeKey("sourceNode");
559         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
560                 sourceNodeConnKey = newInvNodeConnKey("sourceTP");
561         InstanceIdentifier<?> sourceConnID = newNodeConnID(sourceNodeKey, sourceNodeConnKey);
562
563         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
564                 destNodeKey = newInvNodeKey("destNode");
565         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
566                 destNodeConnKey = newInvNodeConnKey("destTP");
567         InstanceIdentifier<?> destConnID = newNodeConnID(destNodeKey, destNodeConnKey);
568
569         Link link = newLink(sourceNodeConnKey.getId().getValue(), newSourceTp(sourceNodeConnKey.getId().getValue()),
570                 newDestTp(destNodeConnKey.getId().getValue()));
571
572         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
573         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
574         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
575         doReturn(Futures.immediateCheckedFuture(Optional.of(link))).when(mockTx).read(LogicalDatastoreType.OPERATIONAL, topologyIID.child(
576                 Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId()))));
577
578         exporter.onLinkRemoved(new LinkRemovedBuilder().setSource(
579                 new NodeConnectorRef(sourceConnID)).setDestination(
580                 new NodeConnectorRef(destConnID)).build());
581
582         waitForSubmit(submitLatch);
583
584         verify(mockTx).delete(LogicalDatastoreType.OPERATIONAL, topologyIID.child(
585                 Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId()))));
586     }
587
588     @Test
589     public void testOnLinkRemovedLinkDoesNotExist() {
590
591         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
592                 sourceNodeKey = newInvNodeKey("sourceNode");
593         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
594                 sourceNodeConnKey = newInvNodeConnKey("sourceTP");
595         InstanceIdentifier<?> sourceConnID = newNodeConnID(sourceNodeKey, sourceNodeConnKey);
596
597         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
598                 destNodeKey = newInvNodeKey("destNode");
599         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
600                 destNodeConnKey = newInvNodeConnKey("destTP");
601         InstanceIdentifier<?> destConnID = newNodeConnID(destNodeKey, destNodeConnKey);
602
603         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
604         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
605         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
606         doReturn(Futures.immediateCheckedFuture(Optional.<Link>absent())).when(mockTx).read(LogicalDatastoreType.OPERATIONAL, topologyIID.child(
607                 Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId()))));
608
609         exporter.onLinkRemoved(new LinkRemovedBuilder().setSource(
610                 new NodeConnectorRef(sourceConnID)).setDestination(
611                 new NodeConnectorRef(destConnID)).build());
612
613         waitForSubmit(submitLatch);
614
615         verify(mockTx, never()).delete(LogicalDatastoreType.OPERATIONAL, topologyIID.child(
616                 Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId()))));
617     }
618
619     private void verifyMockTx(ReadWriteTransaction mockTx) {
620         InOrder inOrder = inOrder(mockTx);
621         inOrder.verify(mockTx, atLeast(0)).submit();
622         inOrder.verify(mockTx, never()).delete(eq(LogicalDatastoreType.OPERATIONAL),
623               any(InstanceIdentifier.class));
624     }
625
626     @SuppressWarnings("rawtypes")
627     private void assertDeletedIDs(InstanceIdentifier[] expDeletedIIDs,
628             ArgumentCaptor<InstanceIdentifier> deletedLinkIDs) {
629         Set<InstanceIdentifier> actualIIDs = new HashSet<>(deletedLinkIDs.getAllValues());
630         for(InstanceIdentifier id: expDeletedIIDs) {
631             assertTrue("Missing expected deleted IID " + id, actualIIDs.contains(id));
632         }
633     }
634
635     private void setReadFutureAsync(final Topology topology,
636             final SettableFuture<Optional<Topology>> readFuture) {
637         new Thread() {
638             @Override
639             public void run() {
640                 Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
641                 readFuture.set(Optional.of(topology));
642             }
643
644         }.start();
645     }
646
647     private void waitForSubmit(CountDownLatch latch) {
648         assertEquals("Transaction submitted", true,
649                 Uninterruptibles.awaitUninterruptibly(latch, 5, TimeUnit.SECONDS));
650     }
651
652     private void waitForDeletes(int expDeleteCalls, final CountDownLatch latch) {
653         boolean done = Uninterruptibles.awaitUninterruptibly(latch, 5, TimeUnit.SECONDS);
654         if(!done) {
655             fail("Expected " + expDeleteCalls + " delete calls. Actual: " +
656                     (expDeleteCalls - latch.getCount()));
657         }
658     }
659
660     private CountDownLatch setupStubbedSubmit(ReadWriteTransaction mockTx) {
661         final CountDownLatch latch = new CountDownLatch(1);
662         doAnswer(new Answer<CheckedFuture<Void, TransactionCommitFailedException>>() {
663             @Override
664             public CheckedFuture<Void, TransactionCommitFailedException> answer(
665                                                             InvocationOnMock invocation) {
666                 latch.countDown();
667                 return Futures.immediateCheckedFuture(null);
668             }
669         }).when(mockTx).submit();
670
671         return latch;
672     }
673
674     @SuppressWarnings("rawtypes")
675     private void setupStubbedDeletes(ReadWriteTransaction mockTx,
676             ArgumentCaptor<InstanceIdentifier> deletedLinkIDs, final CountDownLatch latch) {
677         doAnswer(new Answer<Void>() {
678             @Override
679             public Void answer(InvocationOnMock invocation) {
680                 latch.countDown();
681                 return null;
682             }
683         }).when(mockTx).delete(eq(LogicalDatastoreType.OPERATIONAL), deletedLinkIDs.capture());
684     }
685
686     private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
687                                                                         newInvNodeKey(String id) {
688         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey nodeKey =
689                 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey(
690                         new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.
691                                                                       rev130819.NodeId(id));
692         return nodeKey;
693     }
694
695     private NodeConnectorKey newInvNodeConnKey(String id) {
696         return new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey(
697                 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.
698                                                                NodeConnectorId(id));
699     }
700
701     private KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> newNodeConnID(
702             org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey nodeKey,
703             org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey) {
704         return InstanceIdentifier.create(Nodes.class).child(
705                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
706                 nodeKey).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.
707                         rev130819.node.NodeConnector.class, ncKey);
708     }
709
710     private Link newLink(String id, Source source, Destination dest) {
711         return new LinkBuilder().setLinkId(new LinkId(id))
712                 .setSource(source).setDestination(dest).build();
713     }
714
715     private Destination newDestTp(String id) {
716         return new DestinationBuilder().setDestTp(new TpId(id)).build();
717     }
718
719     private Source newSourceTp(String id) {
720         return new SourceBuilder().setSourceTp(new TpId(id)).build();
721     }
722
723     private Destination newDestNode(String id) {
724         return new DestinationBuilder().setDestNode(new NodeId(id)).build();
725     }
726
727     private Source newSourceNode(String id) {
728         return new SourceBuilder().setSourceNode(new NodeId(id)).build();
729     }
730 }