Fix issue when AE leader differs from prior install snapshot leader
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / behaviors / SnapshotTrackerTest.java
1 /*
2  * Copyright (c) 2014, 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.raft.behaviors;
10
11 import static org.junit.Assert.assertEquals;
12 import com.google.common.base.Optional;
13 import com.google.protobuf.ByteString;
14 import java.io.ByteArrayOutputStream;
15 import java.io.IOException;
16 import java.io.ObjectOutputStream;
17 import java.util.Arrays;
18 import java.util.HashMap;
19 import java.util.Map;
20 import org.junit.Assert;
21 import org.junit.Before;
22 import org.junit.Test;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 public class SnapshotTrackerTest {
27
28     Logger logger = LoggerFactory.getLogger(getClass());
29
30     Map<String, String> data;
31     ByteString byteString;
32     byte[] chunk1;
33     byte[] chunk2;
34     byte[] chunk3;
35
36     @Before
37     public void setup(){
38         data = new HashMap<>();
39         data.put("key1", "value1");
40         data.put("key2", "value2");
41         data.put("key3", "value3");
42
43         byteString = toByteString(data);
44         chunk1 = getNextChunk(byteString, 0, 10);
45         chunk2 = getNextChunk(byteString, 10, 10);
46         chunk3 = getNextChunk(byteString, 20, byteString.size());
47     }
48
49     @Test
50     public void testAddChunk() throws SnapshotTracker.InvalidChunkException {
51         SnapshotTracker tracker1 = new SnapshotTracker(logger, 5, "leader");
52
53         tracker1.addChunk(1, chunk1, Optional.<Integer>absent());
54         tracker1.addChunk(2, chunk2, Optional.<Integer>absent());
55         tracker1.addChunk(3, chunk3, Optional.<Integer>absent());
56
57         // Verify that an InvalidChunkException is thrown when we try to add a chunk to a sealed tracker
58         SnapshotTracker tracker2 = new SnapshotTracker(logger, 2, "leader");
59
60         tracker2.addChunk(1, chunk1, Optional.<Integer>absent());
61         tracker2.addChunk(2, chunk2, Optional.<Integer>absent());
62
63         try {
64             tracker2.addChunk(3, chunk3, Optional.<Integer>absent());
65             Assert.fail();
66         } catch(SnapshotTracker.InvalidChunkException e){
67             e.getMessage().startsWith("Invalid chunk");
68         }
69
70         // The first chunk's index must at least be FIRST_CHUNK_INDEX
71         SnapshotTracker tracker3 = new SnapshotTracker(logger, 2, "leader");
72
73         try {
74             tracker3.addChunk(AbstractLeader.FIRST_CHUNK_INDEX - 1, chunk1, Optional.<Integer>absent());
75             Assert.fail();
76         } catch(SnapshotTracker.InvalidChunkException e){
77
78         }
79
80         // Out of sequence chunk indexes won't work
81         SnapshotTracker tracker4 = new SnapshotTracker(logger, 2, "leader");
82
83         tracker4.addChunk(AbstractLeader.FIRST_CHUNK_INDEX, chunk1, Optional.<Integer>absent());
84
85         try {
86             tracker4.addChunk(AbstractLeader.FIRST_CHUNK_INDEX+2, chunk2, Optional.<Integer>absent());
87             Assert.fail();
88         } catch(SnapshotTracker.InvalidChunkException e){
89
90         }
91
92         // No exceptions will be thrown when invalid chunk is added with the right sequence
93         // If the lastChunkHashCode is missing
94         SnapshotTracker tracker5 = new SnapshotTracker(logger, 2, "leader");
95
96         tracker5.addChunk(AbstractLeader.FIRST_CHUNK_INDEX, chunk1, Optional.<Integer>absent());
97         // Look I can add the same chunk again
98         tracker5.addChunk(AbstractLeader.FIRST_CHUNK_INDEX + 1, chunk1, Optional.<Integer>absent());
99
100         // An exception will be thrown when an invalid chunk is addedd with the right sequence
101         // when the lastChunkHashCode is present
102         SnapshotTracker tracker6 = new SnapshotTracker(logger, 2, "leader");
103
104         tracker6.addChunk(AbstractLeader.FIRST_CHUNK_INDEX, chunk1, Optional.of(-1));
105
106         try {
107             // Here we add a second chunk and tell addChunk that the previous chunk had a hash code 777
108             tracker6.addChunk(AbstractLeader.FIRST_CHUNK_INDEX + 1, chunk2, Optional.of(777));
109             Assert.fail();
110         }catch(SnapshotTracker.InvalidChunkException e){
111
112         }
113
114     }
115
116     @Test
117     public void testGetSnapShot() throws SnapshotTracker.InvalidChunkException {
118
119         // Trying to get a snapshot before all chunks have been received will throw an exception
120         SnapshotTracker tracker1 = new SnapshotTracker(logger, 5, "leader");
121
122         tracker1.addChunk(1, chunk1, Optional.<Integer>absent());
123         try {
124             tracker1.getSnapshot();
125             Assert.fail();
126         } catch(IllegalStateException e){
127
128         }
129
130         SnapshotTracker tracker2 = new SnapshotTracker(logger, 3, "leader");
131
132         tracker2.addChunk(1, chunk1, Optional.of(AbstractLeader.INITIAL_LAST_CHUNK_HASH_CODE));
133         tracker2.addChunk(2, chunk2, Optional.of(Arrays.hashCode(chunk1)));
134         tracker2.addChunk(3, chunk3, Optional.of(Arrays.hashCode(chunk2)));
135
136         byte[] snapshot = tracker2.getSnapshot();
137
138         assertEquals(byteString, ByteString.copyFrom(snapshot));
139     }
140
141     @Test
142     public void testGetCollectedChunks() throws SnapshotTracker.InvalidChunkException {
143         SnapshotTracker tracker1 = new SnapshotTracker(logger, 5, "leader");
144
145         ByteString chunks = ByteString.copyFrom(chunk1).concat(ByteString.copyFrom(chunk2));
146
147         tracker1.addChunk(1, chunk1, Optional.of(AbstractLeader.INITIAL_LAST_CHUNK_HASH_CODE));
148         tracker1.addChunk(2, chunk2, Optional.of(Arrays.hashCode(chunk1)));
149
150         assertEquals(chunks, tracker1.getCollectedChunks());
151     }
152
153     public byte[] getNextChunk (ByteString bs, int offset, int size){
154         int snapshotLength = bs.size();
155         int start = offset;
156         if (size > snapshotLength) {
157             size = snapshotLength;
158         } else {
159             if (start + size > snapshotLength) {
160                 size = snapshotLength - start;
161             }
162         }
163
164         byte[] nextChunk = new byte[size];
165         bs.copyTo(nextChunk, start, 0, size);
166         return nextChunk;
167     }
168
169     private static ByteString toByteString(Map<String, String> state) {
170         ByteArrayOutputStream b = null;
171         ObjectOutputStream o = null;
172         try {
173             try {
174                 b = new ByteArrayOutputStream();
175                 o = new ObjectOutputStream(b);
176                 o.writeObject(state);
177                 byte[] snapshotBytes = b.toByteArray();
178                 return ByteString.copyFrom(snapshotBytes);
179             } finally {
180                 if (o != null) {
181                     o.flush();
182                     o.close();
183                 }
184                 if (b != null) {
185                     b.close();
186                 }
187             }
188         } catch (IOException e) {
189             org.junit.Assert.fail("IOException in converting Hashmap to Bytestring:" + e);
190         }
191         return null;
192     }
193
194
195 }