9d1deecbc608c2d3677fdd681a45b6b235072ab7
[openflowplugin.git] / applications / forwardingrules-sync / src / test / java / org / opendaylight / openflowplugin / applications / frsync / util / ReconcileUtilTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.openflowplugin.applications.frsync.util;
9
10 import com.google.common.base.Splitter;
11 import com.google.common.collect.Maps;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import com.google.common.util.concurrent.SettableFuture;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import org.junit.Assert;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.junit.rules.ExpectedException;
28 import org.junit.runner.RunWith;
29 import org.mockito.ArgumentCaptor;
30 import org.mockito.ArgumentMatchers;
31 import org.mockito.Captor;
32 import org.mockito.Mock;
33 import org.mockito.Mockito;
34 import org.mockito.junit.MockitoJUnitRunner;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierOutput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.opendaylight.yangtools.yang.common.RpcResult;
57 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
58 import org.opendaylight.yangtools.yang.common.Uint32;
59
60 /**
61  * Test for {@link ReconcileUtil}.
62  */
63 @RunWith(MockitoJUnitRunner.class)
64 public class ReconcileUtilTest {
65
66     private static final NodeId NODE_ID = new NodeId("unit-node-id");
67     private static final InstanceIdentifier<Node> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
68             .child(Node.class, new NodeKey(NODE_ID));
69     private static final Splitter COMMA_SPLITTER = Splitter.on(",");
70
71     @Rule
72     public ExpectedException thrown = ExpectedException.none();
73     @Mock
74     private FlowCapableTransactionService flowCapableService;
75     @Captor
76     private ArgumentCaptor<SendBarrierInput> barrierInputCaptor;
77
78     @Test
79     public void testChainBarrierFlush() throws Exception {
80         SettableFuture<RpcResult<Void>> testRabbit = SettableFuture.create();
81         final ListenableFuture<RpcResult<Void>> vehicle =
82                 Futures.transformAsync(testRabbit, ReconcileUtil.chainBarrierFlush(NODE_IDENT, flowCapableService),
83                         MoreExecutors.directExecutor());
84         Mockito.when(flowCapableService.sendBarrier(barrierInputCaptor.capture()))
85                 .thenReturn(RpcResultBuilder.<SendBarrierOutput>success().buildFuture());
86
87         Mockito.verify(flowCapableService, Mockito.never()).sendBarrier(ArgumentMatchers.any());
88         Assert.assertFalse(vehicle.isDone());
89
90         testRabbit.set(RpcResultBuilder.<Void>success().build());
91         Mockito.verify(flowCapableService).sendBarrier(ArgumentMatchers.any());
92         Assert.assertTrue(vehicle.isDone());
93         Assert.assertTrue(vehicle.get().isSuccessful());
94     }
95
96     /**
97      * add one missing group.
98      */
99     @Test
100     public void testResolveAndDivideGroupDiffs1() {
101         final Map<Uint32, Group> installedGroups = createGroups(1, 2, 3);
102
103         final List<Group> pendingGroups = new ArrayList<>();
104         pendingGroups.add(createGroup(2L));
105         pendingGroups.add(createGroup(3L));
106         pendingGroups.add(createGroup(4L));
107
108         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
109                 NODE_ID, installedGroups, pendingGroups);
110
111         Assert.assertEquals(1, plan.size());
112
113         Assert.assertEquals(1, plan.get(0).getItemsToPush().size());
114         Assert.assertEquals(4L, plan.get(0).getItemsToPush().iterator().next().key()
115                 .getGroupId().getValue().longValue());
116         Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
117     }
118
119     /**
120      * add 3 groups with dependencies - 3 steps involved.
121      */
122     @Test
123     public void testResolveAndDivideGroupDiffs2() {
124         final Map<Uint32, Group> installedGroups = createGroups(1);
125
126         final List<Group> pendingGroups = new ArrayList<>();
127         pendingGroups.add(createGroup(2L));
128         pendingGroups.add(createGroupWithPreconditions(3L, 2L, 4L));
129         pendingGroups.add(createGroupWithPreconditions(4L, 2L));
130
131         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
132                 NODE_ID, installedGroups, pendingGroups);
133
134         Assert.assertEquals(3, plan.size());
135
136         Assert.assertEquals(1, plan.get(0).getItemsToPush().size());
137         Assert.assertEquals(2L, plan.get(0).getItemsToPush().iterator().next().key()
138                 .getGroupId().getValue().longValue());
139         Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
140
141         Assert.assertEquals(1, plan.get(1).getItemsToPush().size());
142         Assert.assertEquals(4L, plan.get(1).getItemsToPush().iterator().next().key()
143                 .getGroupId().getValue().longValue());
144         Assert.assertEquals(0, plan.get(1).getItemsToUpdate().size());
145
146         Assert.assertEquals(1, plan.get(2).getItemsToPush().size());
147         Assert.assertEquals(3L, plan.get(2).getItemsToPush().iterator().next().key()
148                 .getGroupId().getValue().longValue());
149         Assert.assertEquals(0, plan.get(2).getItemsToUpdate().size());
150     }
151
152     /**
153      * no actions taken - installed and pending groups are the same.
154      */
155     @Test
156     public void testResolveAndDivideGroupDiffs3() {
157         final Map<Uint32, Group> installedGroups = new HashMap<>();
158         installedGroups.put(Uint32.ONE, createGroup(1L));
159         installedGroups.put(Uint32.valueOf(2), createGroupWithPreconditions(2L, 1L));
160
161         final List<Group> pendingGroups = new ArrayList<>();
162         pendingGroups.add(createGroup(1L));
163         pendingGroups.add(createGroupWithPreconditions(2L, 1L));
164
165         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
166                 NODE_ID, installedGroups, pendingGroups);
167
168         Assert.assertEquals(0, plan.size());
169     }
170
171     /**
172      * update 1 group.
173      */
174     @Test
175     public void testResolveAndDivideGroupDiffs4() {
176         final Map<Uint32, Group> installedGroups = createGroups(1, 2);
177
178         final List<Group> pendingGroups = new ArrayList<>();
179         pendingGroups.add(createGroupWithPreconditions(1L, 2L));
180         pendingGroups.add(createGroup(2L));
181
182         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
183                 NODE_ID, installedGroups, pendingGroups);
184
185         Assert.assertEquals(1, plan.size());
186         Assert.assertEquals(0, plan.get(0).getItemsToPush().size());
187         Assert.assertEquals(1, plan.get(0).getItemsToUpdate().size());
188         final ItemSyncBox.ItemUpdateTuple<Group> firstItemUpdateTuple =
189                 plan.get(0).getItemsToUpdate().iterator().next();
190         Assert.assertEquals(1L, firstItemUpdateTuple.getOriginal().getGroupId().getValue().longValue());
191         Assert.assertEquals(1L, firstItemUpdateTuple.getUpdated().getGroupId().getValue().longValue());
192     }
193
194     /**
195      * no action taken - update 1 group will be ignored.
196      */
197     @Test
198     public void testResolveAndDivideGroupDiffs5() {
199         final Map<Uint32, Group> installedGroups = createGroups(1, 2);
200
201         final List<Group> pendingGroups = new ArrayList<>();
202         pendingGroups.add(createGroupWithPreconditions(1L, 2L));
203         pendingGroups.add(createGroup(2L));
204
205         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
206                 NODE_ID, installedGroups, pendingGroups, false);
207
208         Assert.assertEquals(0, plan.size());
209     }
210
211     /**
212      * should add 1 group but preconditions are not met.
213      */
214     @Test
215     public void testResolveAndDivideGroupDiffs_negative1() {
216         final Map<Uint32, Group> installedGroups = createGroups(1, 2);
217
218         final List<Group> pendingGroups = new ArrayList<>();
219         pendingGroups.add(createGroupWithPreconditions(3L, 4L));
220
221         thrown.expect(IllegalStateException.class);
222         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
223                 NODE_ID, installedGroups, pendingGroups);
224     }
225
226     /**
227      * should update 1 group but preconditions are not met.
228      */
229     @Test
230     public void testResolveAndDivideGroupDiffs_negative2() {
231         final Map<Uint32, Group> installedGroups = createGroups(1, 2);
232
233         final List<Group> pendingGroups = new ArrayList<>();
234         pendingGroups.add(createGroupWithPreconditions(1L, 3L));
235
236         thrown.expect(IllegalStateException.class);
237         final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
238                 NODE_ID, installedGroups, pendingGroups);
239     }
240
241     @Test
242     public void testCheckGroupPrecondition() {
243         final Set<Uint32> installedGroups = new HashSet<>(Arrays.asList(Uint32.ONE, Uint32.valueOf(2)));
244
245         final Group pendingGroup1 = createGroupWithPreconditions(3L, 2L, 4L);
246         Assert.assertFalse(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup1));
247
248         final Group pendingGroup2 = createGroupWithPreconditions(1L, 2L);
249         Assert.assertTrue(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup2));
250
251         final Group pendingGroup3 = createGroupWithPreconditions(1L);
252         Assert.assertTrue(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup3));
253     }
254
255     private static Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
256         final List<Action> actionBag = new ArrayList<>();
257         for (long groupIdPrecondition : requiredId) {
258             final GroupAction groupAction = new GroupActionBuilder()
259                     .setGroupId(groupIdPrecondition)
260                     .build();
261             final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
262                     .setGroupAction(groupAction)
263                     .build();
264             final Action action = new ActionBuilder()
265                     .setAction(groupActionCase)
266                     .build();
267             actionBag.add(action);
268         }
269
270         final Bucket bucket = new BucketBuilder()
271                 .setAction(actionBag)
272                 .build();
273         final Buckets buckets = new BucketsBuilder()
274                 .setBucket(Collections.singletonList(bucket))
275                 .build();
276
277         return new GroupBuilder()
278                 .setGroupId(new GroupId(groupIdValue))
279                 .setBuckets(buckets)
280                 .build();
281     }
282
283     private static Map<Uint32, Group> createGroups(long... groupIds) {
284         final Map<Uint32, Group> ret = Maps.newHashMapWithExpectedSize(groupIds.length);
285         for (long groupId : groupIds) {
286             ret.put(Uint32.valueOf(groupId), createGroup(groupId));
287         }
288         return ret;
289     }
290
291     private static Group createGroup(final long groupIdValue) {
292         final Buckets buckets = new BucketsBuilder()
293                 .setBucket(Collections.emptyList())
294                 .build();
295         return new GroupBuilder()
296                 .setGroupId(new GroupId(groupIdValue))
297                 .setBuckets(buckets)
298                 .build();
299     }
300
301     /**
302      * covers {@link ReconcileUtil#countTotalUpdated(Iterable)} too.
303      */
304     @Test
305     public void testCountTotalAdds() {
306         List<ItemSyncBox<String>> syncPlan = new ArrayList<>();
307         ItemSyncBox<String> syncBox1 = createSyncBox("a,b", "x,y,z");
308         syncPlan.add(syncBox1);
309         syncPlan.add(syncBox1);
310         Assert.assertEquals(4, ReconcileUtil.countTotalPushed(syncPlan));
311         Assert.assertEquals(6, ReconcileUtil.countTotalUpdated(syncPlan));
312     }
313
314     private ItemSyncBox<String> createSyncBox(final String pushes, final String updates) {
315         ItemSyncBox<String> syncBox1 = new ItemSyncBox<>();
316         syncBox1.getItemsToPush().addAll(COMMA_SPLITTER.splitToList(pushes));
317         for (String orig : COMMA_SPLITTER.splitToList(updates)) {
318             syncBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(orig, orig + "_updated"));
319         }
320         return syncBox1;
321     }
322 }