BUG-5280: expand ShardDataTree to cover transaction mechanics
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / datastore / ShardDataTreeTest.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.controller.cluster.datastore;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.mockito.Mockito.doReturn;
14 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediateCanCommit;
15 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediateCommit;
16 import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediatePreCommit;
17 import com.google.common.base.Optional;
18 import com.google.common.base.Ticker;
19 import java.math.BigInteger;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import org.junit.Before;
24 import org.junit.Test;
25 import org.mockito.Mockito;
26 import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats;
27 import org.opendaylight.controller.md.cluster.datastore.model.CarsModel;
28 import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel;
29 import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
32 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
34 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
35 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37
38 public class ShardDataTreeTest extends AbstractTest {
39
40     private final Shard mockShard = Mockito.mock(Shard.class);
41
42
43     private SchemaContext fullSchema;
44
45     @Before
46     public void setUp() {
47         doReturn(true).when(mockShard).canSkipPayload();
48         doReturn(Ticker.systemTicker()).when(mockShard).ticker();
49         doReturn(Mockito.mock(ShardStats.class)).when(mockShard).getShardMBean();
50
51         fullSchema = SchemaContextHelper.full();
52     }
53
54     @Test
55     public void testWrite() throws ExecutionException, InterruptedException {
56         modify(new ShardDataTree(mockShard, fullSchema, TreeType.OPERATIONAL), false, true, true);
57     }
58
59     @Test
60     public void testMerge() throws ExecutionException, InterruptedException {
61         modify(new ShardDataTree(mockShard, fullSchema, TreeType.OPERATIONAL), true, true, true);
62     }
63
64
65     private void modify(final ShardDataTree shardDataTree, final boolean merge, final boolean expectedCarsPresent, final boolean expectedPeoplePresent) throws ExecutionException, InterruptedException {
66
67         assertEquals(fullSchema, shardDataTree.getSchemaContext());
68
69         final ReadWriteShardDataTreeTransaction transaction = shardDataTree.newReadWriteTransaction(nextTransactionId());
70
71         final DataTreeModification snapshot = transaction.getSnapshot();
72
73         assertNotNull(snapshot);
74
75         if(merge){
76             snapshot.merge(CarsModel.BASE_PATH, CarsModel.create());
77             snapshot.merge(PeopleModel.BASE_PATH, PeopleModel.create());
78         } else {
79             snapshot.write(CarsModel.BASE_PATH, CarsModel.create());
80             snapshot.write(PeopleModel.BASE_PATH, PeopleModel.create());
81         }
82
83         final ShardDataTreeCohort cohort = shardDataTree.finishTransaction(transaction);
84
85         immediateCanCommit(cohort);
86         immediatePreCommit(cohort);
87         immediateCommit(cohort);
88
89         final ReadOnlyShardDataTreeTransaction readOnlyShardDataTreeTransaction = shardDataTree.newReadOnlyTransaction(nextTransactionId());
90
91         final DataTreeSnapshot snapshot1 = readOnlyShardDataTreeTransaction.getSnapshot();
92
93         final Optional<NormalizedNode<?, ?>> optional = snapshot1.readNode(CarsModel.BASE_PATH);
94
95         assertEquals(expectedCarsPresent, optional.isPresent());
96
97         final Optional<NormalizedNode<?, ?>> optional1 = snapshot1.readNode(PeopleModel.BASE_PATH);
98
99         assertEquals(expectedPeoplePresent, optional1.isPresent());
100
101     }
102
103     @Test
104     public void bug4359AddRemoveCarOnce() throws ExecutionException, InterruptedException {
105         final ShardDataTree shardDataTree = new ShardDataTree(mockShard, fullSchema, TreeType.OPERATIONAL);
106
107         final List<DataTreeCandidateTip> candidates = new ArrayList<>();
108         candidates.add(addCar(shardDataTree));
109         candidates.add(removeCar(shardDataTree));
110
111         final NormalizedNode<?, ?> expected = getCars(shardDataTree);
112
113         applyCandidates(shardDataTree, candidates);
114
115         final NormalizedNode<?, ?> actual = getCars(shardDataTree);
116
117         assertEquals(expected, actual);
118     }
119
120     @Test
121     public void bug4359AddRemoveCarTwice() throws ExecutionException, InterruptedException {
122         final ShardDataTree shardDataTree = new ShardDataTree(mockShard, fullSchema, TreeType.OPERATIONAL);
123
124         final List<DataTreeCandidateTip> candidates = new ArrayList<>();
125         candidates.add(addCar(shardDataTree));
126         candidates.add(removeCar(shardDataTree));
127         candidates.add(addCar(shardDataTree));
128         candidates.add(removeCar(shardDataTree));
129
130         final NormalizedNode<?, ?> expected = getCars(shardDataTree);
131
132         applyCandidates(shardDataTree, candidates);
133
134         final NormalizedNode<?, ?> actual = getCars(shardDataTree);
135
136         assertEquals(expected, actual);
137     }
138
139     private static NormalizedNode<?, ?> getCars(final ShardDataTree shardDataTree) {
140         final ReadOnlyShardDataTreeTransaction readOnlyShardDataTreeTransaction = shardDataTree.newReadOnlyTransaction(nextTransactionId());
141         final DataTreeSnapshot snapshot1 = readOnlyShardDataTreeTransaction.getSnapshot();
142
143         final Optional<NormalizedNode<?, ?>> optional = snapshot1.readNode(CarsModel.BASE_PATH);
144
145         assertEquals(true, optional.isPresent());
146
147         return optional.get();
148     }
149
150     private static DataTreeCandidateTip addCar(final ShardDataTree shardDataTree) throws ExecutionException, InterruptedException {
151         return doTransaction(shardDataTree, snapshot -> {
152                 snapshot.merge(CarsModel.BASE_PATH, CarsModel.emptyContainer());
153                 snapshot.merge(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode());
154                 snapshot.write(CarsModel.newCarPath("altima"), CarsModel.newCarEntry("altima", new BigInteger("100")));
155             });
156     }
157
158     private static DataTreeCandidateTip removeCar(final ShardDataTree shardDataTree) throws ExecutionException, InterruptedException {
159         return doTransaction(shardDataTree, snapshot -> snapshot.delete(CarsModel.newCarPath("altima")));
160     }
161
162     @FunctionalInterface
163     private static interface DataTreeOperation {
164         void execute(DataTreeModification snapshot);
165     }
166
167     private static DataTreeCandidateTip doTransaction(final ShardDataTree shardDataTree, final DataTreeOperation operation)
168             throws ExecutionException, InterruptedException {
169         final ReadWriteShardDataTreeTransaction transaction = shardDataTree.newReadWriteTransaction(nextTransactionId());
170         final DataTreeModification snapshot = transaction.getSnapshot();
171         operation.execute(snapshot);
172         final ShardDataTreeCohort cohort = shardDataTree.finishTransaction(transaction);
173
174         immediateCanCommit(cohort);
175         immediatePreCommit(cohort);
176         final DataTreeCandidateTip candidate = cohort.getCandidate();
177         immediateCommit(cohort);
178
179         return candidate;
180     }
181
182     private static DataTreeCandidateTip applyCandidates(final ShardDataTree shardDataTree, final List<DataTreeCandidateTip> candidates)
183             throws ExecutionException, InterruptedException {
184         final ReadWriteShardDataTreeTransaction transaction = shardDataTree.newReadWriteTransaction(nextTransactionId());
185         final DataTreeModification snapshot = transaction.getSnapshot();
186         for(final DataTreeCandidateTip candidateTip : candidates){
187             DataTreeCandidates.applyToModification(snapshot, candidateTip);
188         }
189         final ShardDataTreeCohort cohort = shardDataTree.finishTransaction(transaction);
190
191         immediateCanCommit(cohort);
192         immediatePreCommit(cohort);
193         final DataTreeCandidateTip candidate = cohort.getCandidate();
194         immediateCommit(cohort);
195
196         return candidate;
197     }
198
199 }