/** * 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 com.google.common.base.Splitter; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; 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; /** * Test for {@link ReconcileUtil}. */ @RunWith(MockitoJUnitRunner.class) public class ReconcileUtilTest { private static final NodeId NODE_ID = new NodeId("unit-node-id"); private final InstanceIdentifier<Node> NODE_IDENT = InstanceIdentifier.create(Nodes.class) .child(Node.class, new NodeKey(NODE_ID)); private static final Splitter COMMA_SPLITTER = Splitter.on(","); @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()); } /** * add one missing group * * @throws Exception */ @Test public void testResolveAndDivideGroupDiffs1() 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.resolveAndDivideGroupDiffs( NODE_ID, installedGroups, pendingGroups); Assert.assertEquals(1, plan.size()); Assert.assertEquals(1, plan.get(0).getItemsToPush().size()); Assert.assertEquals(4L, plan.get(0).getItemsToPush().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 testResolveAndDivideGroupDiffs2() 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.resolveAndDivideGroupDiffs( NODE_ID, installedGroups, pendingGroups); Assert.assertEquals(3, plan.size()); Assert.assertEquals(1, plan.get(0).getItemsToPush().size()); Assert.assertEquals(2L, plan.get(0).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue()); Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size()); Assert.assertEquals(1, plan.get(1).getItemsToPush().size()); Assert.assertEquals(4L, plan.get(1).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue()); Assert.assertEquals(0, plan.get(1).getItemsToUpdate().size()); Assert.assertEquals(1, plan.get(2).getItemsToPush().size()); Assert.assertEquals(3L, plan.get(2).getItemsToPush().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 testResolveAndDivideGroupDiffs3() 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.resolveAndDivideGroupDiffs( NODE_ID, installedGroups, pendingGroups); Assert.assertEquals(0, plan.size()); } /** * update 1 group * * @throws Exception */ @Test public void testResolveAndDivideGroupDiffs4() 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.resolveAndDivideGroupDiffs( NODE_ID, installedGroups, pendingGroups); Assert.assertEquals(1, plan.size()); Assert.assertEquals(0, plan.get(0).getItemsToPush().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 testResolveAndDivideGroupDiffs5() 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.resolveAndDivideGroupDiffs( NODE_ID, installedGroups, pendingGroups, false); Assert.assertEquals(0, plan.size()); } /** * should add 1 group but preconditions are not met * * @throws Exception */ @Test public void testResolveAndDivideGroupDiffs_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.resolveAndDivideGroupDiffs( NODE_ID, installedGroups, pendingGroups); } /** * should update 1 group but preconditions are not met * * @throws Exception */ @Test public void testResolveAndDivideGroupDiffs_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.resolveAndDivideGroupDiffs( 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(); } /** * covers {@link ReconcileUtil#countTotalUpdated(Iterable)} too * * @throws Exception */ @Test public void testCountTotalAdds() throws Exception { List<ItemSyncBox<String>> syncPlan = new ArrayList<>(); ItemSyncBox<String> syncBox1 = createSyncBox("a,b", "x,y,z"); syncPlan.add(syncBox1); syncPlan.add(syncBox1); Assert.assertEquals(4, ReconcileUtil.countTotalPushed(syncPlan)); Assert.assertEquals(6, ReconcileUtil.countTotalUpdated(syncPlan)); } private ItemSyncBox<String> createSyncBox(final String pushes, final String updates) { ItemSyncBox<String> syncBox1 = new ItemSyncBox<>(); syncBox1.getItemsToPush().addAll(COMMA_SPLITTER.splitToList(pushes)); for (String orig : COMMA_SPLITTER.splitToList(updates)) { syncBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(orig, orig + "_updated")); } return syncBox1; } }