--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link FlowForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FlowForwarderTest {
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final TableKey tableKey = new TableKey((short) 2);
+ private final FlowId flowId = new FlowId("test_Flow");
+ private final FlowKey flowKey = new FlowKey(flowId);
+ private final Match emptyMatch = new MatchBuilder().build();
+ private final Flow flow = new FlowBuilder()
+ .setId(flowId)
+ .setTableId((short) 2)
+ .setMatch(emptyMatch)
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final InstanceIdentifier<Table> tableII = flowCapableNodePath.child(Table.class, tableKey);
+ private final InstanceIdentifier<Flow> flowPath = tableII.child(Flow.class, flowKey);
+
+ private FlowForwarder flowForwarder;
+
+ @Mock
+ private SalFlowService salFlowService;
+ @Captor
+ private ArgumentCaptor<AddFlowInput> addFlowInputCpt;
+ @Captor
+ private ArgumentCaptor<UpdateFlowInput> updateFlowInputCpt;
+ @Captor
+ private ArgumentCaptor<RemoveFlowInput> removeFlowInputCpt;
+
+
+ @Before
+ public void setUp() throws Exception {
+ flowForwarder = new FlowForwarder(salFlowService);
+ }
+
+ @Test
+ public void addTest() throws Exception {
+ Mockito.when(salFlowService.addFlow(addFlowInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new AddFlowOutputBuilder()
+ .setTransactionId(new TransactionId(BigInteger.ONE))
+ .build()).buildFuture());
+
+ final Future<RpcResult<AddFlowOutput>> addResult = flowForwarder.add(flowPath, flow, flowCapableNodePath);
+
+ Mockito.verify(salFlowService).addFlow(Matchers.<AddFlowInput>any());
+ final AddFlowInput flowInput = addFlowInputCpt.getValue();
+ Assert.assertEquals(2, flowInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowInput.getMatch());
+ Assert.assertEquals(null, flowInput.getInstructions());
+ Assert.assertEquals(nodePath, flowInput.getNode().getValue());
+ Assert.assertEquals(flowPath, flowInput.getFlowRef().getValue());
+ Assert.assertEquals(null, flowInput.isStrict());
+
+
+ final RpcResult<AddFlowOutput> addFlowOutputRpcResult = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(addFlowOutputRpcResult.isSuccessful());
+ final AddFlowOutput resultValue = addFlowOutputRpcResult.getResult();
+ Assert.assertEquals(1, resultValue.getTransactionId().getValue().intValue());
+ }
+
+ @Test
+ public void updateTest() throws Exception {
+ Mockito.when(salFlowService.updateFlow(updateFlowInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new UpdateFlowOutputBuilder()
+ .setTransactionId(new TransactionId(BigInteger.ONE))
+ .build()).buildFuture());
+
+ final Instructions originalInstructions = new InstructionsBuilder()
+ .setInstruction(Collections.singletonList(new InstructionBuilder()
+ .setInstruction(new ApplyActionsCaseBuilder()
+ .setApplyActions(new ApplyActionsBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new DropActionCaseBuilder()
+ .build())
+ .build())
+ ).build()
+ ).build())
+ .build())
+ ).build();
+
+ final Flow flowUpdated = new FlowBuilder(flow)
+ .setInstructions(originalInstructions)
+ .setMatch(new MatchBuilder().build())
+ .build();
+
+ final Future<RpcResult<UpdateFlowOutput>> updateResult = flowForwarder.update(flowPath, flow, flowUpdated, flowCapableNodePath);
+
+ Mockito.verify(salFlowService).updateFlow(Matchers.<UpdateFlowInput>any());
+ final UpdateFlowInput updateFlowInput = updateFlowInputCpt.getValue();
+ final OriginalFlow flowOrigInput = updateFlowInput.getOriginalFlow();
+ final UpdatedFlow flowInput = updateFlowInput.getUpdatedFlow();
+
+ Assert.assertEquals(nodePath, updateFlowInput.getNode().getValue());
+ Assert.assertEquals(flowPath, updateFlowInput.getFlowRef().getValue());
+
+ Assert.assertEquals(2, flowInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowInput.getMatch());
+ Assert.assertEquals(originalInstructions, flowInput.getInstructions());
+ Assert.assertEquals(true, flowInput.isStrict());
+
+ Assert.assertEquals(2, flowOrigInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowOrigInput.getMatch());
+ Assert.assertEquals(null, flowOrigInput.getInstructions());
+ Assert.assertEquals(true, flowOrigInput.isStrict());
+
+
+ final RpcResult<UpdateFlowOutput> updateFlowOutputRpcResult = updateResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(updateFlowOutputRpcResult.isSuccessful());
+ final UpdateFlowOutput resultValue = updateFlowOutputRpcResult.getResult();
+ Assert.assertEquals(1, resultValue.getTransactionId().getValue().intValue());
+ }
+
+ @Test
+ public void removeTest() throws Exception {
+ Mockito.when(salFlowService.removeFlow(removeFlowInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new RemoveFlowOutputBuilder()
+ .setTransactionId(new TransactionId(BigInteger.ONE))
+ .build()).buildFuture());
+
+ final Flow removeFlow = new FlowBuilder(flow).build();
+ final Future<RpcResult<RemoveFlowOutput>> removeResult = flowForwarder.remove(flowPath, removeFlow, flowCapableNodePath);
+
+ Mockito.verify(salFlowService).removeFlow(Matchers.<RemoveFlowInput>any());
+ final RemoveFlowInput flowInput = removeFlowInputCpt.getValue();
+ Assert.assertEquals(2, flowInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowInput.getMatch());
+ Assert.assertEquals(null, flowInput.getInstructions());
+ Assert.assertEquals(true, flowInput.isStrict());
+
+
+ final RpcResult<RemoveFlowOutput> removeFlowOutputRpcResult = removeResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(removeFlowOutputRpcResult.isSuccessful());
+ final RemoveFlowOutput resultValue = removeFlowOutputRpcResult.getResult();
+ Assert.assertEquals(1, resultValue.getTransactionId().getValue().intValue());
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * Test for {@link ForwardingRulesSyncProvider}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ForwardingRulesSyncProviderTest {
+
+ private ForwardingRulesSyncProvider provider;
+ @Mock
+ private DataBroker dataBroker;
+ @Mock
+ private RpcConsumerRegistry rpcRegistry;
+ @Mock
+ private BindingAwareBroker broker;
+ @Mock
+ private BindingAwareBroker.ProviderContext providerContext;
+
+ @Before
+ public void setUp() throws Exception {
+ Mockito.when(rpcRegistry.getRpcService(Matchers.<Class<? extends RpcService>>any()))
+ .thenAnswer(new Answer<RpcService>() {
+ @Override
+ public RpcService answer(final InvocationOnMock invocation) throws Throwable {
+ Class<? extends RpcService> serviceType = (Class<? extends RpcService>) invocation.getArguments()[0];
+ return Mockito.mock(serviceType);
+ }
+ });
+
+ provider = new ForwardingRulesSyncProvider(broker, dataBroker, rpcRegistry);
+
+ Mockito.verify(rpcRegistry).getRpcService(SalFlowService.class);
+ Mockito.verify(rpcRegistry).getRpcService(SalGroupService.class);
+ Mockito.verify(rpcRegistry).getRpcService(SalMeterService.class);
+ Mockito.verify(rpcRegistry).getRpcService(SalTableService.class);
+ Mockito.verify(rpcRegistry).getRpcService(FlowCapableTransactionService.class);
+
+ Mockito.verify(broker).registerProvider(provider);
+ }
+
+ @Test
+ public void testOnSessionInitiated() throws Exception {
+ provider.onSessionInitiated(providerContext);
+
+ Mockito.verify(dataBroker, Mockito.times(2)).registerDataTreeChangeListener(
+ Matchers.<DataTreeIdentifier<FlowCapableNode>>any(),
+ Matchers.<DataTreeChangeListener<FlowCapableNode>>any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link GroupForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class GroupForwarderTest {
+
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final GroupId groupId = new GroupId(42L);
+ private final GroupKey groupKey = new GroupKey(groupId);
+ private final Group group = new GroupBuilder()
+ .setGroupId(groupId)
+ .setGroupName("test-group")
+ .setBuckets(new BucketsBuilder().build())
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final InstanceIdentifier<Group> groupPath = flowCapableNodePath.child(Group.class, groupKey);
+
+ @Mock
+ private SalGroupService salGroupService;
+ @Captor
+ private ArgumentCaptor<AddGroupInput> addGroupInputCpt;
+ @Captor
+ private ArgumentCaptor<RemoveGroupInput> removeGroupInputCpt;
+ @Captor
+ private ArgumentCaptor<UpdateGroupInput> updateGroupInputCpt;
+
+ private TransactionId txId;
+
+ private GroupForwarder groupForwarder;
+
+ @Before
+ public void setUp() throws Exception {
+ groupForwarder = new GroupForwarder(salGroupService);
+ txId = new TransactionId(BigInteger.ONE);
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ Mockito.when(salGroupService.removeGroup(removeGroupInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new RemoveGroupOutputBuilder()
+ .setTransactionId(txId)
+ .build())
+ .buildFuture()
+ );
+
+ final Future<RpcResult<RemoveGroupOutput>> addResult = groupForwarder.remove(groupPath, group, flowCapableNodePath);
+
+ Mockito.verify(salGroupService).removeGroup(Matchers.<RemoveGroupInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<RemoveGroupOutput> result = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(result.isSuccessful());
+
+ Assert.assertEquals(1, result.getResult().getTransactionId().getValue().intValue());
+
+ final RemoveGroupInput removeGroupInput = removeGroupInputCpt.getValue();
+ Assert.assertEquals(groupPath, removeGroupInput.getGroupRef().getValue());
+ Assert.assertNull(removeGroupInput.getBuckets());
+ Assert.assertEquals(nodePath, removeGroupInput.getNode().getValue());
+ Assert.assertEquals("test-group", removeGroupInput.getGroupName());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Mockito.when(salGroupService.updateGroup(updateGroupInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new UpdateGroupOutputBuilder()
+ .setTransactionId(txId)
+ .build())
+ .buildFuture()
+ );
+
+ Group groupOriginal = new GroupBuilder(group).build();
+ Group groupUpdate = new GroupBuilder(group)
+ .setGroupName("another-test")
+ .build();
+
+ final Future<RpcResult<UpdateGroupOutput>> addResult = groupForwarder.update(groupPath, groupOriginal, groupUpdate,
+ flowCapableNodePath);
+
+ Mockito.verify(salGroupService).updateGroup(Matchers.<UpdateGroupInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<UpdateGroupOutput> result = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(result.isSuccessful());
+
+ Assert.assertEquals(1, result.getResult().getTransactionId().getValue().intValue());
+
+ final UpdateGroupInput updateGroupInput = updateGroupInputCpt.getValue();
+ Assert.assertEquals(groupPath, updateGroupInput.getGroupRef().getValue());
+ Assert.assertEquals(nodePath, updateGroupInput.getNode().getValue());
+ Assert.assertNotNull(updateGroupInput.getOriginalGroup().getBuckets());
+ Assert.assertNotNull(updateGroupInput.getUpdatedGroup().getBuckets());
+
+ Assert.assertEquals("test-group", updateGroupInput.getOriginalGroup().getGroupName());
+ Assert.assertEquals("another-test", updateGroupInput.getUpdatedGroup().getGroupName());
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ Mockito.when(salGroupService.addGroup(addGroupInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new AddGroupOutputBuilder()
+ .setTransactionId(txId)
+ .build())
+ .buildFuture()
+ );
+
+ final Future<RpcResult<AddGroupOutput>> addResult = groupForwarder.add(groupPath, group, flowCapableNodePath);
+
+ Mockito.verify(salGroupService).addGroup(Matchers.<AddGroupInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<AddGroupOutput> result = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(result.isSuccessful());
+
+ Assert.assertEquals(1, result.getResult().getTransactionId().getValue().intValue());
+
+ final AddGroupInput addGroupInput = addGroupInputCpt.getValue();
+ Assert.assertEquals(groupPath, addGroupInput.getGroupRef().getValue());
+ Assert.assertEquals(nodePath, addGroupInput.getNode().getValue());
+ Assert.assertNotNull(addGroupInput.getBuckets());
+ Assert.assertEquals("test-group", addGroupInput.getGroupName());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link MeterForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class MeterForwarderTest {
+
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final MeterId meterId = new MeterId(42L);
+ private final MeterKey meterKey = new MeterKey(meterId);
+ private final Meter meter = new MeterBuilder()
+ .setMeterId(meterId)
+ .setMeterName("test-meter")
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final InstanceIdentifier<Meter> meterPath = flowCapableNodePath.child(Meter.class, meterKey);
+
+ @Mock
+ private SalMeterService salMeterService;
+ @Captor
+ private ArgumentCaptor<AddMeterInput> addMeterInputCpt;
+ @Captor
+ private ArgumentCaptor<RemoveMeterInput> removeMeterInputCpt;
+ @Captor
+ private ArgumentCaptor<UpdateMeterInput> updateMeterInputCpt;
+
+ private TransactionId txId;
+
+ private MeterForwarder meterForwarder;
+
+ @Before
+ public void setUp() throws Exception {
+ meterForwarder = new MeterForwarder(salMeterService);
+ txId = new TransactionId(BigInteger.ONE);
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ Mockito.when(salMeterService.removeMeter(removeMeterInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(new RemoveMeterOutputBuilder()
+ .setTransactionId(txId)
+ .build()).buildFuture()
+ );
+
+ Meter removeMeter = new MeterBuilder(meter).build();
+
+ final Future<RpcResult<RemoveMeterOutput>> removeResult = meterForwarder.remove(meterPath, removeMeter, flowCapableNodePath);
+ Mockito.verify(salMeterService).removeMeter(Matchers.<RemoveMeterInput>any());
+
+ Assert.assertTrue(removeResult.isDone());
+ final RpcResult<RemoveMeterOutput> meterResult = removeResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(meterResult.isSuccessful());
+
+ Assert.assertEquals(1, meterResult.getResult().getTransactionId().getValue().intValue());
+
+ final RemoveMeterInput removeMeterInput = removeMeterInputCpt.getValue();
+ Assert.assertEquals(meterPath, removeMeterInput.getMeterRef().getValue());
+ Assert.assertEquals(nodePath, removeMeterInput.getNode().getValue());
+ Assert.assertEquals("test-meter", removeMeterInput.getMeterName());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Mockito.when(salMeterService.updateMeter(updateMeterInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(new UpdateMeterOutputBuilder()
+ .setTransactionId(txId)
+ .build()).buildFuture()
+ );
+
+ Meter meterOriginal = new MeterBuilder(meter).build();
+ Meter meterUpdate = new MeterBuilder(meter)
+ .setMeterName("another-test")
+ .build();
+
+ final Future<RpcResult<UpdateMeterOutput>> updateResult = meterForwarder.update(meterPath, meterOriginal, meterUpdate,
+ flowCapableNodePath);
+ Mockito.verify(salMeterService).updateMeter(Matchers.<UpdateMeterInput>any());
+
+ Assert.assertTrue(updateResult.isDone());
+ final RpcResult<UpdateMeterOutput> meterResult = updateResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(meterResult.isSuccessful());
+
+ Assert.assertEquals(1, meterResult.getResult().getTransactionId().getValue().intValue());
+
+ final UpdateMeterInput updateMeterInput = updateMeterInputCpt.getValue();
+ Assert.assertEquals(meterPath, updateMeterInput.getMeterRef().getValue());
+ Assert.assertEquals(nodePath, updateMeterInput.getNode().getValue());
+
+ Assert.assertEquals("test-meter", updateMeterInput.getOriginalMeter().getMeterName());
+ Assert.assertEquals("another-test", updateMeterInput.getUpdatedMeter().getMeterName());
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ Mockito.when(salMeterService.addMeter(addMeterInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(new AddMeterOutputBuilder()
+ .setTransactionId(txId)
+ .build()).buildFuture()
+ );
+
+ final Future<RpcResult<AddMeterOutput>> addResult = meterForwarder.add(meterPath, meter, flowCapableNodePath);
+ Mockito.verify(salMeterService).addMeter(Matchers.<AddMeterInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<AddMeterOutput> meterResult = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(meterResult.isSuccessful());
+
+ Assert.assertEquals(1, meterResult.getResult().getTransactionId().getValue().intValue());
+
+ final AddMeterInput addMeterInput = addMeterInputCpt.getValue();
+ Assert.assertEquals(meterPath, addMeterInput.getMeterRef().getValue());
+ Assert.assertEquals(nodePath, addMeterInput.getNode().getValue());
+ Assert.assertEquals("test-meter", addMeterInput.getMeterName());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collections;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * Test for {@link SimplifiedConfigListener}.
+ */
+@SuppressWarnings("deprecation")
+@RunWith(MockitoJUnitRunner.class)
+public class SimplifiedConfigListenerTest {
+
+ @Mock
+ private SyncReactor reactor;
+ @Mock
+ private DataBroker db;
+ @Mock
+ private DataTreeModification<FlowCapableNode> dataTreeModification;
+ @Mock
+ private ReadOnlyTransaction roTx;
+ @Mock
+ private DataObjectModification<FlowCapableNode> configModification;
+
+ private InstanceIdentifier<FlowCapableNode> nodePath;
+ private SimplifiedConfigListener nodeListenerConfig;
+
+ @Before
+ public void setUp() throws Exception {
+ final FlowCapableNodeSnapshotDao configSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeSnapshotDao operationalSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeDao operationalDao = new FlowCapableNodeCachedDao(operationalSnaphot,
+ new FlowCapableNodeOdlDao(db, LogicalDatastoreType.OPERATIONAL));
+
+
+ nodeListenerConfig = new SimplifiedConfigListener(reactor, configSnaphot, operationalDao);
+ nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId("testNode")))
+ .augmentation(FlowCapableNode.class);
+ }
+
+ @Test
+ public void testDSLogicalType() throws Exception {
+ Assert.assertEquals(LogicalDatastoreType.CONFIGURATION, nodeListenerConfig.dsType());
+ }
+
+ @Test
+ public void testOnDataTreeChanged() throws Exception {
+ final FlowCapableNode configTree = Mockito.mock(FlowCapableNode.class);
+ final FlowCapableNode operationalTree = Mockito.mock(FlowCapableNode.class);
+ final DataTreeIdentifier<FlowCapableNode> dataTreeIdentifier =
+ new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, nodePath);
+
+ Mockito.when(dataTreeModification.getRootPath()).thenReturn(dataTreeIdentifier);
+ Mockito.when(dataTreeModification.getRootNode()).thenReturn(configModification);
+ Mockito.when(configModification.getDataAfter()).thenReturn(configTree);
+ Mockito.when(db.newReadOnlyTransaction()).thenReturn(roTx);
+ Mockito.doReturn(Futures.immediateCheckedFuture(Optional.of(operationalTree))).when(
+ roTx).read(LogicalDatastoreType.OPERATIONAL, nodePath);
+ Mockito.when(reactor.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(),Matchers.<FlowCapableNode>any(),Matchers.<FlowCapableNode>any()))
+ .thenReturn(Futures.immediateFuture(Boolean.TRUE));
+
+ nodeListenerConfig.onDataTreeChanged(Collections.singleton(dataTreeModification));
+
+ Mockito.verify(reactor).syncup(nodePath, configTree, operationalTree);
+ Mockito.verify(roTx).close();
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collections;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * Test for {@link SimplifiedOperationalListener}.
+ */
+@SuppressWarnings("deprecation")
+@RunWith(MockitoJUnitRunner.class)
+public class SimplifiedOperationalListenerTest {
+
+ @Mock
+ private SyncReactor reactor;
+ @Mock
+ private DataBroker db;
+ @Mock
+ private DataTreeModification<Node> dataTreeModification;
+ @Mock
+ private ReadOnlyTransaction roTx;
+ @Mock
+ private DataObjectModification<Node> operationalModification;
+
+ private NodeId nodeId;
+ private InstanceIdentifier<Node> nodePath;
+ private InstanceIdentifier<FlowCapableNode> fcNodePath;
+ private SimplifiedOperationalListener nodeListenerOperational;
+
+ @SuppressWarnings("deprecation")
+ @Before
+ public void setUp() throws Exception {
+ final FlowCapableNodeSnapshotDao configSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeSnapshotDao operationalSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeDao configDao = new FlowCapableNodeCachedDao(configSnaphot,
+ new FlowCapableNodeOdlDao(db, LogicalDatastoreType.CONFIGURATION));
+
+ nodeId = new NodeId("testNode");
+ nodeListenerOperational = new SimplifiedOperationalListener(reactor, operationalSnaphot, configDao);
+ nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(nodeId));
+ fcNodePath = nodePath.augmentation(FlowCapableNode.class);
+ }
+
+ @Test
+ public void testDSLogicalType() throws Exception {
+ Assert.assertEquals(LogicalDatastoreType.OPERATIONAL, nodeListenerOperational.dsType());
+ }
+
+ @Test
+ public void testOnDataTreeChanged() throws Exception {
+ final FlowCapableNode configTree = Mockito.mock(FlowCapableNode.class);
+ final Node mockOperationalNode = Mockito.mock(Node.class);
+ final FlowCapableNode mockOperationalFlowCapableNode = Mockito.mock(FlowCapableNode.class);
+ Mockito.when(mockOperationalNode.getAugmentation(FlowCapableNode.class))
+ .thenReturn(mockOperationalFlowCapableNode);
+ Mockito.when(mockOperationalNode.getId()).thenReturn(nodeId);
+
+ final DataTreeIdentifier<Node> dataTreeIdentifier =
+ new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, nodePath);
+
+ Mockito.when(dataTreeModification.getRootPath()).thenReturn(dataTreeIdentifier);
+ Mockito.when(dataTreeModification.getRootNode()).thenReturn(operationalModification);
+ Mockito.when(operationalModification.getDataAfter()).thenReturn(mockOperationalNode);
+ Mockito.when(db.newReadOnlyTransaction()).thenReturn(roTx);
+ Mockito.doReturn(Futures.immediateCheckedFuture(Optional.of(configTree))).when(
+ roTx).read(LogicalDatastoreType.CONFIGURATION, fcNodePath);
+ Mockito.when(reactor.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(),Matchers.<FlowCapableNode>any(),Matchers.<FlowCapableNode>any()))
+ .thenReturn(Futures.immediateFuture(Boolean.TRUE));
+
+ nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
+
+ Mockito.verify(reactor).syncup(fcNodePath, configTree, mockOperationalFlowCapableNode);
+ Mockito.verify(roTx).close();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.BandId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.DropBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.MeterBandHeadersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.meter.band.headers.MeterBandHeaderBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test for {@link SyncReactorImpl}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class SyncReactorImplTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncReactorImplTest.class);
+
+ private static final NodeId NODE_ID = new NodeId("unit-nodeId");
+ private static final InstanceIdentifier<FlowCapableNode> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID)).augmentation(FlowCapableNode.class);
+
+ private SyncReactorImpl reactor;
+ @Mock
+ private DataBroker db;
+ @Mock
+ private GroupForwarder groupCommitter;
+ @Mock
+ private FlowForwarder flowCommitter;
+ @Mock
+ private MeterForwarder meterCommitter;
+ @Mock
+ private TableForwarder tableCommitter;
+ @Mock
+ private FlowCapableTransactionService flowCapableTxService;
+
+ @Captor
+ private ArgumentCaptor<Group> groupCaptor;
+ @Captor
+ private ArgumentCaptor<Group> groupUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<TableFeatures> tableFeaturesCaptor;
+
+ private SyncCrudCounters counters;
+
+ @Before
+ public void setUp() throws Exception {
+ Mockito.when(flowCapableTxService.sendBarrier(Matchers.<SendBarrierInput>any()))
+ .thenReturn(RpcResultBuilder.success((Void) null).buildFuture());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).add(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).update(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).remove(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).add(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).update(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).remove(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).add(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).update(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).remove(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(tableCommitter).update(
+ Matchers.<InstanceIdentifier<TableFeatures>>any(), Matchers.<TableFeatures>any(), Matchers.<TableFeatures>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ reactor = new SyncReactorImpl();
+ reactor.setMeterForwarder(meterCommitter);
+ reactor.setTableForwarder(tableCommitter);
+ reactor.setGroupForwarder(groupCommitter);
+ reactor.setFlowForwarder(flowCommitter);
+ reactor.setTransactionService(flowCapableTxService);
+
+ counters = new SyncCrudCounters();
+ }
+
+ private <O> Answer<Future<RpcResult<O>>> createSalServiceFutureAnswer() {
+ return new Answer<Future<RpcResult<O>>>() {
+ @Override
+ public Future<RpcResult<O>> answer(final InvocationOnMock invocation) throws Throwable {
+ return RpcResultBuilder.<O>success().buildFuture();
+ }
+ };
+ }
+
+ private Group createGroup(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.<Bucket>emptyList())
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ private Group createGroupWithAction(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(new BucketBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(new Uri("ut-port-1"))
+ .build())
+ .build())
+ .build()))
+ .build()))
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ private Flow createFlow(final String flowIdValue, final int priority) {
+ return new FlowBuilder()
+ .setId(new FlowId(flowIdValue))
+ .setPriority(priority)
+ .setTableId((short) 42)
+ .setMatch(new MatchBuilder().build())
+ .build();
+ }
+
+ private Flow createFlowWithInstruction(final String flowIdValue, final int priority) {
+ return new FlowBuilder()
+ .setId(new FlowId(flowIdValue))
+ .setPriority(priority)
+ .setTableId((short) 42)
+ .setMatch(new MatchBuilder().build())
+ .setInstructions(new InstructionsBuilder()
+ .setInstruction(Collections.singletonList(new InstructionBuilder()
+ .setInstruction(new ApplyActionsCaseBuilder()
+ .setApplyActions(new ApplyActionsBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(new Uri("ut-port-1"))
+ .build())
+ .build())
+ .build()))
+ .build())
+ .build())
+ .build()))
+ .build())
+ .build();
+ }
+
+ private Meter createMeter(final Long meterIdValue) {
+ return new MeterBuilder()
+ .setMeterId(new MeterId(meterIdValue))
+ .build();
+ }
+
+ private Meter createMeterWithBody(final Long meterIdValue) {
+ return new MeterBuilder()
+ .setMeterId(new MeterId(meterIdValue))
+ .setMeterBandHeaders(new MeterBandHeadersBuilder()
+ .setMeterBandHeader(Collections.singletonList(new MeterBandHeaderBuilder()
+ .setBandId(new BandId(42L))
+ .setBandType(new DropBuilder()
+ .setDropRate(43L)
+ .build())
+ .build()))
+ .build())
+ .build();
+ }
+
+ private Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
+ final List<Action> actionBag = new ArrayList<>();
+ for (long groupIdPrecondition : requiredId) {
+ final GroupAction groupAction = new GroupActionBuilder()
+ .setGroupId(groupIdPrecondition)
+ .build();
+ final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
+ .setGroupAction(groupAction)
+ .build();
+ final Action action = new ActionBuilder()
+ .setAction(groupActionCase)
+ .build();
+ actionBag.add(action);
+ }
+
+ final Bucket bucket = new BucketBuilder()
+ .setAction(actionBag)
+ .build();
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(bucket))
+ .build();
+
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ @Test
+ public void testRemoveRedundantFlows() throws Exception {
+ Mockito.when(flowCommitter.remove(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveFlowOutputBuilder().build()).buildFuture());
+
+ final Table tableCfg = new TableBuilder()
+ .setId((short) 0)
+ .setFlow(Arrays.asList(
+ createFlow("f1", 1), createFlow("f2", 2)))
+ .build();
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(tableCfg))
+ .build();
+
+ final Table tableOpe = new TableBuilder()
+ .setId((short) 0)
+ .setFlow(Arrays.asList(
+ createFlow("f1", 1), createFlow("f2", 2), createFlow("f3", 3), createFlow("f4", 4)))
+ .build();
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(tableOpe))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.removeRedundantFlows(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingFlows() throws Exception {
+ Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
+
+ final Table tableCfg = new TableBuilder()
+ .setId((short) 0)
+ .setFlow(Arrays.asList(
+ createFlow("f1", 1), createFlow("f2", 2), createFlow("f3", 3), createFlow("f4", 4)))
+ .build();
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(tableCfg))
+ .build();
+
+ final Table tableOpe = new TableBuilder()
+ .setId((short) 0)
+ .setFlow(Arrays.asList(
+ createFlow("f1", 1), createFlow("f2", 2)))
+ .build();
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(tableOpe))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.addMissingFlows(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ //inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingFlows_withUpdate() throws Exception {
+ Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
+
+ Mockito.when(flowCommitter.update(Matchers.<InstanceIdentifier<Flow>>any(),
+ flowUpdateCaptor.capture(), flowUpdateCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateFlowOutputBuilder().build()).buildFuture());
+
+ final Table tableCfg = new TableBuilder()
+ .setId((short) 0)
+ .setFlow(Arrays.asList(
+ createFlowWithInstruction("f1", 1), createFlow("f2", 2),
+ createFlow("f3", 3), createFlow("f4", 4)))
+ .build();
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(tableCfg))
+ .build();
+
+ final Table tableOpe = new TableBuilder()
+ .setId((short) 0)
+ .setFlow(Arrays.asList(
+ createFlow("f1", 1), createFlow("f2", 2)))
+ .build();
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(tableOpe))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.addMissingFlows(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final List<Flow> flowUpdateCaptorAllValues = flowUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, flowUpdateCaptorAllValues.size());
+ Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ // update f1
+ inOrderFlow.verify(flowCommitter).update(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ // add f3, f4
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ //inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingMeters() throws Exception {
+ Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setMeter(Arrays.asList(
+ createMeter(1L), createMeter(2L), createMeter(3L), createMeter(4L)
+ ))
+ .build();
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setMeter(Arrays.asList(
+ createMeter(1L), createMeter(3L)
+ ))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.addMissingMeters(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, metercaptorAllValues.size());
+ Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeter.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderMeter.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingMeters_withUpdate() throws Exception {
+ Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
+
+ Mockito.when(meterCommitter.update(Matchers.<InstanceIdentifier<Meter>>any(),
+ meterUpdateCaptor.capture(), meterUpdateCaptor.capture(), Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateMeterOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setMeter(Arrays.asList(
+ createMeterWithBody(1L), createMeter(2L), createMeter(3L), createMeter(4L)
+ ))
+ .build();
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setMeter(Arrays.asList(
+ createMeter(1L), createMeter(3L)
+ ))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.addMissingMeters(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> meterCaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, meterCaptorAllValues.size());
+ Assert.assertEquals(2L, meterCaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, meterCaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+
+ final List<Meter> meterUpdateCaptorAllValues = meterUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, meterUpdateCaptorAllValues.size());
+ Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeters = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeters.verify(meterCommitter).update(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ inOrderMeters.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //inOrderMeters.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderMeters.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantMeters() throws Exception {
+ Mockito.when(meterCommitter.remove(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveMeterOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setMeter(Arrays.asList(
+ createMeter(1L), createMeter(3L)
+ ))
+ .build();
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setMeter(Arrays.asList(
+ createMeter(1L), createMeter(2L), createMeter(3L), createMeter(4L)
+ ))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.removeRedundantMeters(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, metercaptorAllValues.size());
+ Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeter.verify(meterCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderMeter.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingGroups() throws Exception {
+ Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setGroup(Arrays.asList(
+ createGroup(1L), createGroup(2L),
+ createGroupWithPreconditions(3L, 2L),
+ createGroupWithPreconditions(4L, 2L),
+ createGroupWithPreconditions(5L, 3L, 4L)))
+ .build();
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(createGroup(1L)))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.addMissingGroups(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
+ // add 2
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 3, 4
+ inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 5
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroups.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingGroups_withUpdate() throws Exception {
+ Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
+
+ Mockito.when(groupCommitter.update(Matchers.<InstanceIdentifier<Group>>any(),
+ groupUpdateCaptor.capture(), groupUpdateCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateGroupOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setGroup(Arrays.asList(
+ createGroupWithAction(1L), createGroup(2L),
+ createGroupWithPreconditions(3L, 2L),
+ createGroupWithPreconditions(4L, 2L),
+ createGroupWithPreconditions(5L, 3L, 4L)))
+ .build();
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(createGroup(1L)))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.addMissingGroups(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final List<Group> groupUpdateCaptorAllValues = groupUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, groupUpdateCaptorAllValues.size());
+ Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(1).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
+
+ // add 2, update 1
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(groupCommitter).update(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ // add 3, 4
+ inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 5
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroups.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantGroups() throws Exception {
+ Mockito.when(groupCommitter.remove(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveGroupOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setGroup(new ArrayList<>(Arrays.asList(
+ createGroup(1L), createGroup(2L),
+ createGroupWithPreconditions(3L, 2L),
+ createGroupWithPreconditions(4L, 2L),
+ createGroupWithPreconditions(5L, 3L, 4L))))
+ .build();
+ final FlowCapableNode config = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(createGroup(1L)))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.removeRedundantGroups(
+ NODE_IDENT, config, operational, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroup = Mockito.inOrder(flowCapableTxService, groupCommitter);
+ // remove 5
+ inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // remove 3, 4
+ inOrderGroup.verify(groupCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // remove 2
+ inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroup.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testSyncup() throws Exception {
+ final FlowCapableNode configFcn = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(createGroup(1L)))
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setFlow(Collections.singletonList(createFlow("f1", 1)))
+ .build()))
+ .setMeter(Collections.singletonList(createMeter(1L)))
+ .build();
+
+ final FlowCapableNode operationalFcn = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(createGroup(2L)))
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setFlow(Collections.singletonList(createFlow("f2", 2)))
+ .build()))
+ .setMeter(Collections.singletonList(createMeter(2L)))
+ .build();
+
+ final ListenableFuture<Boolean> syncupResult = reactor.syncup(NODE_IDENT, configFcn, operationalFcn);
+ try {
+ final Boolean voidRpcResult = syncupResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(voidRpcResult);
+
+ final InOrder inOrder = Mockito.inOrder(flowCommitter, meterCommitter, groupCommitter,
+ tableCommitter, flowCapableTxService);
+
+ inOrder.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.same(NODE_IDENT));
+ inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrder.verify(meterCommitter).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.same(NODE_IDENT));
+ //inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrder.verify(flowCommitter).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.same(NODE_IDENT));
+ //inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrder.verify(flowCommitter).remove(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.same(NODE_IDENT));
+ inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrder.verify(meterCommitter).remove(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.same(NODE_IDENT));
+ //inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrder.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.same(NODE_IDENT));
+ inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrder.verifyNoMoreInteractions();
+
+ } catch (Exception e) {
+ LOG.warn("syncup failed", e);
+ Assert.fail("syncup failed: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdateTableFeatures() throws Exception {
+ Mockito.when(tableCommitter.update(Matchers.<InstanceIdentifier<TableFeatures>>any(),
+ Matchers.isNull(TableFeatures.class), tableFeaturesCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateTableOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setId((short) 1)
+ .build()))
+ .setTableFeatures(Collections.singletonList(new TableFeaturesBuilder()
+ .setName("test table features")
+ .setTableId((short) 1)
+ .build()))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = reactor.updateTableFeatures(
+ NODE_IDENT, operational);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<TableFeatures> groupCaptorAllValues = tableFeaturesCaptor.getAllValues();
+ Assert.assertEquals(0, groupCaptorAllValues.size());
+ //Assert.assertEquals("test table features", groupCaptorAllValues.get(0).getName());
+ //Assert.assertEquals(1, groupCaptorAllValues.get(0).getTableId().intValue());
+
+ Mockito.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link TableForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class TableForwarderTest {
+
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final Short tableId = (short) 42;
+ private final TableKey tableKey = new TableKey(tableId);
+ private final TableFeaturesKey tableFeaturesKey = new TableFeaturesKey(tableId);
+ private final TableFeatures tableFeatures = new TableFeaturesBuilder()
+ .setName("test-table")
+ .setTableId(tableId)
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final KeyedInstanceIdentifier<Table, TableKey> tablePath = flowCapableNodePath
+ .child(Table.class, tableKey);
+ private final InstanceIdentifier<TableFeatures> tableFeaturesPath = flowCapableNodePath
+ .child(TableFeatures.class, tableFeaturesKey);
+
+ @Captor
+ private ArgumentCaptor<UpdateTableInput> updateTableInputCpt;
+ @Mock
+ private SalTableService salTableService;
+
+ private TransactionId txId;
+
+ private TableForwarder tableForwarder;
+
+
+ @Before
+ public void setUp() throws Exception {
+ tableForwarder = new TableForwarder(salTableService);
+ txId = new TransactionId(BigInteger.ONE);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Mockito.when(salTableService.updateTable(updateTableInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new UpdateTableOutputBuilder()
+ .setTransactionId(txId)
+ .build()
+ ).buildFuture()
+ );
+
+ final TableFeatures tableFeaturesUpdate = new TableFeaturesBuilder(tableFeatures)
+ .setName("another-table")
+ .build();
+ final Future<RpcResult<UpdateTableOutput>> updateResult = tableForwarder.update(
+ tableFeaturesPath, tableFeatures, tableFeaturesUpdate, flowCapableNodePath);
+
+ Mockito.verify(salTableService).updateTable(Matchers.<UpdateTableInput>any());
+
+ Assert.assertTrue(updateResult.isDone());
+ final RpcResult<UpdateTableOutput> updateTableResult = updateResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(updateTableResult.isSuccessful());
+
+ Assert.assertEquals(1, updateTableResult.getResult().getTransactionId().getValue().intValue());
+
+ final UpdateTableInput updateTableInput = updateTableInputCpt.getValue();
+ Assert.assertEquals(tablePath, updateTableInput.getTableRef().getValue());
+ Assert.assertEquals(nodePath, updateTableInput.getNode().getValue());
+
+ Assert.assertEquals(1, updateTableInput.getOriginalTable().getTableFeatures().size());
+ Assert.assertEquals("test-table", updateTableInput.getOriginalTable().getTableFeatures().get(0).getName());
+ Assert.assertEquals(1, updateTableInput.getUpdatedTable().getTableFeatures().size());
+ Assert.assertEquals("another-table", updateTableInput.getUpdatedTable().getTableFeatures().get(0).getName());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Test for {@link ReconcileUtil}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ReconcileUtilTest {
+
+ private static final NodeId NODE_ID = new NodeId("unit-node-id");
+ private InstanceIdentifier<Node> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID));
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+ @Mock
+ private FlowCapableTransactionService flowCapableService;
+ @Captor
+ private ArgumentCaptor<SendBarrierInput> barrierInputCaptor;
+
+ @Test
+ public void testChainBarrierFlush() throws Exception {
+ SettableFuture<RpcResult<Void>> testRabbit = SettableFuture.create();
+ final ListenableFuture<RpcResult<Void>> vehicle =
+ Futures.transform(testRabbit, ReconcileUtil.chainBarrierFlush(NODE_IDENT, flowCapableService));
+ Mockito.when(flowCapableService.sendBarrier(barrierInputCaptor.capture()))
+ .thenReturn(RpcResultBuilder.<Void>success().buildFuture());
+
+ Mockito.verify(flowCapableService, Mockito.never()).sendBarrier(Matchers.<SendBarrierInput>any());
+ Assert.assertFalse(vehicle.isDone());
+
+ testRabbit.set(RpcResultBuilder.<Void>success().build());
+ Mockito.verify(flowCapableService).sendBarrier(Matchers.<SendBarrierInput>any());
+ Assert.assertTrue(vehicle.isDone());
+ Assert.assertTrue(vehicle.get().isSuccessful());
+ }
+
+ @Test
+ public void testCreateRpcResultCondenser() throws Exception {
+
+ }
+
+ /**
+ * add one missing group
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups1() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+ installedGroups.put(3L, createGroup(3L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroup(2L));
+ pendingGroups.add(createGroup(3L));
+ pendingGroups.add(createGroup(4L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(1, plan.size());
+
+ Assert.assertEquals(1, plan.get(0).getItemsToAdd().size());
+ Assert.assertEquals(4L, plan.get(0).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
+ }
+
+ /**
+ * add 3 groups with dependencies - 3 steps involved
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups2() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroup(2L));
+ pendingGroups.add(createGroupWithPreconditions(3L, 2L, 4L));
+ pendingGroups.add(createGroupWithPreconditions(4L, 2L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(3, plan.size());
+
+ Assert.assertEquals(1, plan.get(0).getItemsToAdd().size());
+ Assert.assertEquals(2L, plan.get(0).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
+
+ Assert.assertEquals(1, plan.get(1).getItemsToAdd().size());
+ Assert.assertEquals(4L, plan.get(1).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(1).getItemsToUpdate().size());
+
+ Assert.assertEquals(1, plan.get(2).getItemsToAdd().size());
+ Assert.assertEquals(3L, plan.get(2).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(2).getItemsToUpdate().size());
+ }
+
+ /**
+ * no actions taken - installed and pending groups are the same
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups3() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroupWithPreconditions(2L, 1L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroup(1L));
+ pendingGroups.add(createGroupWithPreconditions(2L, 1L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(0, plan.size());
+ }
+
+ /**
+ * update 1 group
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups4() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(1L, 2L));
+ pendingGroups.add(createGroup(2L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(1, plan.size());
+ Assert.assertEquals(0, plan.get(0).getItemsToAdd().size());
+ Assert.assertEquals(1, plan.get(0).getItemsToUpdate().size());
+ final ItemSyncBox.ItemUpdateTuple<Group> firstItemUpdateTuple = plan.get(0).getItemsToUpdate().iterator().next();
+ Assert.assertEquals(1L, firstItemUpdateTuple.getOriginal().getGroupId().getValue().longValue());
+ Assert.assertEquals(1L, firstItemUpdateTuple.getUpdated().getGroupId().getValue().longValue());
+ }
+
+ /**
+ * no action taken - update 1 group will be ignored
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups5() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(1L, 2L));
+ pendingGroups.add(createGroup(2L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups, false);
+
+ Assert.assertEquals(0, plan.size());
+ }
+
+ /**
+ * should add 1 group but preconditions are not met
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups_negative1() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(3L, 4L));
+
+ thrown.expect(IllegalStateException.class);
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups);
+ }
+
+ /**
+ * should update 1 group but preconditions are not met
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroups_negative2() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(1L, 3L));
+
+ thrown.expect(IllegalStateException.class);
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ NODE_ID, installedGroups, pendingGroups);
+ }
+
+ @Test
+ public void testCheckGroupPrecondition() throws Exception {
+ final Set<Long> installedGroups = new HashSet<>(Arrays.asList(new Long[]{1L, 2L}));
+
+ final Group pendingGroup1 = createGroupWithPreconditions(3L, 2L, 4L);
+ Assert.assertFalse(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup1));
+
+ final Group pendingGroup2 = createGroupWithPreconditions(1L, 2L);
+ Assert.assertTrue(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup2));
+
+ final Group pendingGroup3 = createGroupWithPreconditions(1L);
+ Assert.assertTrue(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup3));
+ }
+
+ private Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
+ final List<Action> actionBag = new ArrayList<>();
+ for (long groupIdPrecondition : requiredId) {
+ final GroupAction groupAction = new GroupActionBuilder()
+ .setGroupId(groupIdPrecondition)
+ .build();
+ final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
+ .setGroupAction(groupAction)
+ .build();
+ final Action action = new ActionBuilder()
+ .setAction(groupActionCase)
+ .build();
+ actionBag.add(action);
+ }
+
+ final Bucket bucket = new BucketBuilder()
+ .setAction(actionBag)
+ .build();
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(bucket))
+ .build();
+
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ private Group createGroup(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.<Bucket>emptyList())
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2016 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.openflowplugin.applications.frsync.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.openflowplugin.applications.frsync.SemaphoreKeeper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test for {@link SemaphoreKeeperGuavaImpl}.
+ */
+public class SemaphoreKeeperTest {
+ private static final Logger LOG = LoggerFactory.getLogger(SemaphoreKeeperTest.class);
+ private SemaphoreKeeperGuavaImpl<String> semaphoreKeeper;
+ final String key = "11";
+
+ @Before
+ public void setUp() throws Exception {
+ semaphoreKeeper = new SemaphoreKeeperGuavaImpl(1, true);
+ }
+
+ @Test
+ public void testSummonGuard() throws Exception {
+ Semaphore semaphore1 = semaphoreKeeper.summonGuard(key);
+ final int g1FingerPrint = semaphore1.hashCode();
+ Semaphore semaphore2 = semaphoreKeeper.summonGuard(key);
+ final int g2FingerPrint = semaphore2.hashCode();
+
+ Assert.assertSame(semaphore1, semaphore2);
+ Assert.assertEquals(1, semaphore1.availablePermits());
+
+ semaphore1.acquire();
+ semaphore1.release();
+ Assert.assertEquals(1, semaphore1.availablePermits());
+ semaphore1 = null;
+ System.gc();
+
+ semaphore2.acquire();
+ semaphore2.release();
+ Assert.assertEquals(1, semaphore2.availablePermits());
+ semaphore2 = null;
+ Assert.assertEquals(g1FingerPrint, g2FingerPrint);
+
+ System.gc();
+ final Semaphore semaphore3 = semaphoreKeeper.summonGuard(key);
+ Assert.assertNotEquals(g1FingerPrint, semaphore3.hashCode());
+ }
+
+ @Test
+ public void testReleaseGuard() throws Exception {
+ for (int total = 1; total <= 10; total++) {
+ LOG.info("test run: {}", total);
+ final Worker task = new Worker(semaphoreKeeper, key);
+
+ final ExecutorService executorService = new ThreadPoolExecutor(5, 5,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>()) {
+ @Override
+ protected void afterExecute(final Runnable r, final Throwable t) {
+ super.afterExecute(r, t);
+ if (t != null) {
+ LOG.error("pool thread crashed", t);
+ }
+ }
+ };
+
+ final int steps = 10;
+ for (int i = 0; i < steps; i++) {
+ executorService.submit(task);
+ }
+ Thread.sleep(50L);
+ LOG.info("STARTING new serie");
+ System.gc();
+
+ for (int i = 0; i < steps; i++) {
+ executorService.submit(task);
+ }
+ Thread.sleep(1000L);
+ System.gc();
+
+ executorService.shutdown();
+ final boolean terminated = executorService.awaitTermination(10, TimeUnit.SECONDS);
+ if (!terminated) {
+ LOG.warn("pool stuck, forcing termination");
+ executorService.shutdownNow();
+ Assert.fail("pool failed to finish gracefully");
+ }
+
+ final int counterSize = task.getCounterSize();
+ LOG.info("final counter = {}", counterSize);
+ Assert.assertEquals(20, counterSize);
+ }
+ }
+
+ private static class Worker implements Runnable {
+ private final SemaphoreKeeper<String> keeper;
+ private final String key;
+ private ConcurrentMap<Integer, Integer> counter = new ConcurrentHashMap<>();
+ private volatile int index = 0;
+
+ public Worker(SemaphoreKeeper<String> keeper, final String key) {
+ this.keeper = keeper;
+ this.key = key;
+ }
+
+ @Override
+ public void run() {
+ try {
+ final Semaphore guard = keeper.summonGuard(key);
+ Thread.sleep(2L);
+ guard.acquire();
+ counter.putIfAbsent(index, 0);
+ counter.put(index, counter.get(index) + 1);
+ LOG.debug("queue: {} [{}] - {}", guard.getQueueLength(), guard.hashCode(), counter.size());
+ index++;
+ guard.release();
+ } catch (Exception e) {
+ LOG.warn("acquiring failed.. ", e);
+ }
+ }
+
+ public int getCounterSize() {
+ return counter.size();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">\r
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">\r
+\r
+ <appender name="console" class="org.apache.log4j.ConsoleAppender">\r
+ <layout class="org.apache.log4j.PatternLayout">\r
+ <param name="ConversionPattern" value="%-6p %d{HH:mm:ss.SSS} [%10.10t] %30.30c %x - %m%n"/>\r
+ </layout>\r
+ </appender>\r
+\r
+ <logger name="org.opendaylight.openflowplugin.applications.frsync" additivity="false">\r
+ <level value="TRACE"/>\r
+ <appender-ref ref="console"/>\r
+ </logger>\r
+\r
+ <root>\r
+ <priority value="INFO"/>\r
+ <appender-ref ref="console"/>\r
+ </root>\r
+</log4j:configuration>
\ No newline at end of file