Refactor ReplicatedLogImpl to separate class
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / AbstractReplicatedLogImplTest.java
1 /*
2  * Copyright (c) 2014 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.controller.cluster.raft;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertTrue;
14 import akka.japi.Procedure;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import org.junit.After;
19 import org.junit.Assert;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload;
23 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockReplicatedLogEntry;
24
25 /**
26 *
27 */
28 public class AbstractReplicatedLogImplTest {
29
30     private MockAbstractReplicatedLogImpl replicatedLogImpl;
31
32     @Before
33     public void setUp() {
34         replicatedLogImpl = new MockAbstractReplicatedLogImpl();
35         // create a set of initial entries in the in-memory log
36         replicatedLogImpl.append(new MockReplicatedLogEntry(1, 0, new MockPayload("A")));
37         replicatedLogImpl.append(new MockReplicatedLogEntry(1, 1, new MockPayload("B")));
38         replicatedLogImpl.append(new MockReplicatedLogEntry(1, 2, new MockPayload("C")));
39         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 3, new MockPayload("D")));
40
41     }
42
43     @After
44     public void tearDown() {
45         replicatedLogImpl.journal.clear();
46         replicatedLogImpl.setSnapshotIndex(-1);
47         replicatedLogImpl.setSnapshotTerm(-1);
48         replicatedLogImpl = null;
49     }
50
51     @Test
52     public void testIndexOperations() {
53
54         // check if the values returned are correct, with snapshotIndex = -1
55         assertEquals("B", replicatedLogImpl.get(1).getData().toString());
56         assertEquals("D", replicatedLogImpl.last().getData().toString());
57         assertEquals(3, replicatedLogImpl.lastIndex());
58         assertEquals(2, replicatedLogImpl.lastTerm());
59         assertEquals(2, replicatedLogImpl.getFrom(2).size());
60         assertEquals(4, replicatedLogImpl.size());
61         assertTrue(replicatedLogImpl.isPresent(2));
62         assertFalse(replicatedLogImpl.isPresent(4));
63         assertFalse(replicatedLogImpl.isInSnapshot(2));
64
65         // now create a snapshot of 3 entries, with 1 unapplied entry left in the log
66         // It removes the entries which have made it to snapshot
67         // and updates the snapshot index and term
68         Map<Long, String> state = takeSnapshot(3);
69
70         // check the values after the snapshot.
71         // each index value passed in the test is the logical index (log entry index)
72         // which gets mapped to the list's physical index
73         assertEquals("D", replicatedLogImpl.get(3).getData().toString());
74         assertEquals("D", replicatedLogImpl.last().getData().toString());
75         assertNull(replicatedLogImpl.get(1));
76         assertEquals(3, replicatedLogImpl.lastIndex());
77         assertEquals(2, replicatedLogImpl.lastTerm());
78         assertEquals(0, replicatedLogImpl.getFrom(2).size());
79         assertEquals(1, replicatedLogImpl.size());
80         assertFalse(replicatedLogImpl.isPresent(2));
81         assertTrue(replicatedLogImpl.isPresent(3));
82         assertFalse(replicatedLogImpl.isPresent(4));
83         assertTrue(replicatedLogImpl.isInSnapshot(2));
84
85         // append few more entries
86         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
87         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
88         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
89         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
90
91         // check their values as well
92         assertEquals(5, replicatedLogImpl.size());
93         assertEquals("D", replicatedLogImpl.get(3).getData().toString());
94         assertEquals("E", replicatedLogImpl.get(4).getData().toString());
95         assertEquals("H", replicatedLogImpl.last().getData().toString());
96         assertEquals(3, replicatedLogImpl.lastTerm());
97         assertEquals(7, replicatedLogImpl.lastIndex());
98         assertTrue(replicatedLogImpl.isPresent(7));
99         assertFalse(replicatedLogImpl.isInSnapshot(7));
100         assertEquals(1, replicatedLogImpl.getFrom(7).size());
101         assertEquals(2, replicatedLogImpl.getFrom(6).size());
102
103         // take a second snapshot with 5 entries with 0 unapplied entries left in the log
104         state = takeSnapshot(5);
105
106         assertEquals(0, replicatedLogImpl.size());
107         assertNull(replicatedLogImpl.last());
108         assertNull(replicatedLogImpl.get(7));
109         assertNull(replicatedLogImpl.get(1));
110         assertFalse(replicatedLogImpl.isPresent(7));
111         assertTrue(replicatedLogImpl.isInSnapshot(7));
112         assertEquals(0, replicatedLogImpl.getFrom(7).size());
113         assertEquals(0, replicatedLogImpl.getFrom(6).size());
114
115     }
116
117     @Test
118     public void testGetFromWithMax(){
119         List<ReplicatedLogEntry> from = replicatedLogImpl.getFrom(0, 1);
120         Assert.assertEquals(1, from.size());
121         Assert.assertEquals(1, from.get(0).getTerm());
122
123         from = replicatedLogImpl.getFrom(0, 20);
124         Assert.assertEquals(4, from.size());
125         Assert.assertEquals(2, from.get(3).getTerm());
126
127         from = replicatedLogImpl.getFrom(1, 2);
128         Assert.assertEquals(2, from.size());
129         Assert.assertEquals(1, from.get(1).getTerm());
130
131     }
132
133     @Test
134     public void testSnapshotPreCommit() {
135         //add 4 more entries
136         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E")));
137         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F")));
138         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G")));
139         replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H")));
140
141         //sending negative values should not cause any changes
142         replicatedLogImpl.snapshotPreCommit(-1, -1);
143         assertEquals(8, replicatedLogImpl.size());
144         assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
145
146         replicatedLogImpl.snapshotPreCommit(4, 3);
147         assertEquals(3, replicatedLogImpl.size());
148         assertEquals(4, replicatedLogImpl.getSnapshotIndex());
149
150         replicatedLogImpl.snapshotPreCommit(6, 3);
151         assertEquals(1, replicatedLogImpl.size());
152         assertEquals(6, replicatedLogImpl.getSnapshotIndex());
153
154         replicatedLogImpl.snapshotPreCommit(7, 3);
155         assertEquals(0, replicatedLogImpl.size());
156         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
157
158         //running it again on an empty list should not throw exception
159         replicatedLogImpl.snapshotPreCommit(7, 3);
160         assertEquals(0, replicatedLogImpl.size());
161         assertEquals(7, replicatedLogImpl.getSnapshotIndex());
162
163     }
164
165     @Test
166     public void testIsPresent() {
167         assertTrue(replicatedLogImpl.isPresent(0));
168         assertTrue(replicatedLogImpl.isPresent(1));
169         assertTrue(replicatedLogImpl.isPresent(2));
170         assertTrue(replicatedLogImpl.isPresent(3));
171
172         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("D")));
173         replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
174         replicatedLogImpl.snapshotCommit();
175
176         assertFalse(replicatedLogImpl.isPresent(0));
177         assertFalse(replicatedLogImpl.isPresent(1));
178         assertFalse(replicatedLogImpl.isPresent(2));
179         assertFalse(replicatedLogImpl.isPresent(3));
180         assertTrue(replicatedLogImpl.isPresent(4));
181
182         replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
183         replicatedLogImpl.snapshotCommit();
184         assertFalse(replicatedLogImpl.isPresent(4));
185
186         replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("D")));
187         assertTrue(replicatedLogImpl.isPresent(5));
188     }
189
190     // create a snapshot for test
191     public Map<Long, String> takeSnapshot(final int numEntries) {
192         Map<Long, String> map = new HashMap<>(numEntries);
193         List<ReplicatedLogEntry> entries = replicatedLogImpl.getEntriesTill(numEntries);
194         for (ReplicatedLogEntry entry : entries) {
195             map.put(entry.getIndex(), entry.getData().toString());
196         }
197
198         int term = (int) replicatedLogImpl.lastTerm();
199         int lastIndex = (int) entries.get(entries.size() - 1).getIndex();
200         entries.clear();
201         replicatedLogImpl.setSnapshotTerm(term);
202         replicatedLogImpl.setSnapshotIndex(lastIndex);
203
204         return map;
205
206     }
207     class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl {
208         @Override
209         public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
210         }
211
212         @Override
213         public void removeFromAndPersist(final long index) {
214         }
215
216         @Override
217         public int dataSize() {
218             return -1;
219         }
220
221         public List<ReplicatedLogEntry> getEntriesTill(final int index) {
222             return journal.subList(0, index);
223         }
224
225         @Override
226         public void appendAndPersist(ReplicatedLogEntry replicatedLogEntry, Procedure<ReplicatedLogEntry> callback) {
227         }
228     }
229 }