35ecd77d390aa41d477aacdad438bb7a62fcee9e
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / ReplicatedLogImplTest.java
1 /*
2  * Copyright (c) 2015 Brocade Communications 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.mockito.ArgumentMatchers.argThat;
12 import static org.mockito.ArgumentMatchers.same;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.reset;
16 import static org.mockito.Mockito.verify;
17 import static org.mockito.Mockito.verifyNoMoreInteractions;
18
19 import akka.japi.Procedure;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import java.util.Collections;
22 import java.util.function.Consumer;
23 import org.junit.Before;
24 import org.junit.Test;
25 import org.mockito.ArgumentCaptor;
26 import org.mockito.ArgumentMatcher;
27 import org.mockito.Mock;
28 import org.mockito.MockitoAnnotations;
29 import org.mockito.internal.matchers.Same;
30 import org.opendaylight.controller.cluster.DataPersistenceProvider;
31 import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload;
32 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
33 import org.opendaylight.controller.cluster.raft.persisted.DeleteEntries;
34 import org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Unit tests for ReplicatedLogImpl.
40  *
41  * @author Thomas Pantelis
42  */
43 public class ReplicatedLogImplTest {
44     private static final Logger LOG = LoggerFactory.getLogger(RaftActorRecoverySupportTest.class);
45
46     @Mock
47     private DataPersistenceProvider mockPersistence;
48
49     @Mock
50     private RaftActorBehavior mockBehavior;
51
52     private RaftActorContext context;
53     private final DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
54
55     @Before
56     public void setup() {
57         MockitoAnnotations.initMocks(this);
58
59         context = new RaftActorContextImpl(null, null, "test",
60                 new ElectionTermImpl(mockPersistence, "test", LOG), -1, -1, Collections.emptyMap(),
61                 configParams, mockPersistence, applyState -> { }, LOG,  MoreExecutors.directExecutor());
62     }
63
64     private void verifyPersist(final Object message) throws Exception {
65         verifyPersist(message, new Same(message), true);
66     }
67
68     @SuppressWarnings({ "unchecked", "rawtypes" })
69     private void verifyPersist(final Object message, final ArgumentMatcher<?> matcher, final boolean async)
70             throws Exception {
71         ArgumentCaptor<Procedure> procedure = ArgumentCaptor.forClass(Procedure.class);
72         if (async) {
73             verify(mockPersistence).persistAsync(argThat(matcher), procedure.capture());
74         } else {
75             verify(mockPersistence).persist(argThat(matcher), procedure.capture());
76         }
77
78         procedure.getValue().apply(message);
79     }
80
81     @SuppressWarnings("unchecked")
82     @Test
83     public void testAppendAndPersistExpectingNoCapture() throws Exception {
84         ReplicatedLog log = ReplicatedLogImpl.newInstance(context);
85
86         ReplicatedLogEntry logEntry1 = new SimpleReplicatedLogEntry(1, 1, new MockPayload("1"));
87
88         log.appendAndPersist(logEntry1, null, true);
89
90         verifyPersist(logEntry1);
91
92         assertEquals("size", 1, log.size());
93
94         reset(mockPersistence);
95
96         ReplicatedLogEntry logEntry2 = new SimpleReplicatedLogEntry(2, 1, new MockPayload("2"));
97         Consumer<ReplicatedLogEntry> mockCallback = mock(Consumer.class);
98         log.appendAndPersist(logEntry2, mockCallback, true);
99
100         verifyPersist(logEntry2);
101
102         verify(mockCallback).accept(same(logEntry2));
103
104         assertEquals("size", 2, log.size());
105     }
106
107     @SuppressWarnings("unchecked")
108     @Test
109     public void testAppendAndPersisWithDuplicateEntry() throws Exception {
110         ReplicatedLog log = ReplicatedLogImpl.newInstance(context);
111
112         Consumer<ReplicatedLogEntry> mockCallback = mock(Consumer.class);
113         ReplicatedLogEntry logEntry = new SimpleReplicatedLogEntry(1, 1, new MockPayload("1"));
114
115         log.appendAndPersist(logEntry, mockCallback, true);
116
117         verifyPersist(logEntry);
118
119         assertEquals("size", 1, log.size());
120
121         reset(mockPersistence, mockCallback);
122
123         log.appendAndPersist(logEntry, mockCallback, true);
124
125         verifyNoMoreInteractions(mockPersistence, mockCallback);
126
127         assertEquals("size", 1, log.size());
128     }
129
130     @Test
131     public void testAppendAndPersistExpectingCaptureDueToJournalCount() throws Exception {
132         configParams.setSnapshotBatchCount(2);
133
134         doReturn(1L).when(mockBehavior).getReplicatedToAllIndex();
135
136         ReplicatedLog log = ReplicatedLogImpl.newInstance(context);
137
138         final ReplicatedLogEntry logEntry1 = new SimpleReplicatedLogEntry(2, 1, new MockPayload("2"));
139         final ReplicatedLogEntry logEntry2 = new SimpleReplicatedLogEntry(3, 1, new MockPayload("3"));
140
141         log.appendAndPersist(logEntry1, null, true);
142         verifyPersist(logEntry1);
143
144         reset(mockPersistence);
145
146         log.appendAndPersist(logEntry2, null, true);
147         verifyPersist(logEntry2);
148
149
150         assertEquals("size", 2, log.size());
151     }
152
153     @Test
154     public void testAppendAndPersistExpectingCaptureDueToDataSize() throws Exception {
155         doReturn(1L).when(mockBehavior).getReplicatedToAllIndex();
156
157         context.setTotalMemoryRetriever(() -> 100);
158
159         ReplicatedLog log = ReplicatedLogImpl.newInstance(context);
160
161         int dataSize = 600;
162         ReplicatedLogEntry logEntry = new SimpleReplicatedLogEntry(2, 1, new MockPayload("2", dataSize));
163
164         log.appendAndPersist(logEntry, null, true);
165         verifyPersist(logEntry);
166
167         reset(mockPersistence);
168
169         logEntry = new SimpleReplicatedLogEntry(3, 1, new MockPayload("3", 5));
170
171         log.appendAndPersist(logEntry, null, true);
172         verifyPersist(logEntry);
173
174         assertEquals("size", 2, log.size());
175     }
176
177     @Test
178     public void testRemoveFromAndPersist() throws Exception {
179
180         ReplicatedLog log = ReplicatedLogImpl.newInstance(context);
181
182         log.append(new SimpleReplicatedLogEntry(0, 1, new MockPayload("0")));
183         log.append(new SimpleReplicatedLogEntry(1, 1, new MockPayload("1")));
184         log.append(new SimpleReplicatedLogEntry(2, 1, new MockPayload("2")));
185
186         log.removeFromAndPersist(1);
187
188         DeleteEntries deleteEntries = new DeleteEntries(1);
189         verifyPersist(deleteEntries, match(deleteEntries), false);
190
191         assertEquals("size", 1, log.size());
192
193         reset(mockPersistence);
194
195         log.removeFromAndPersist(1);
196
197         verifyNoMoreInteractions(mockPersistence);
198     }
199
200     @Test
201     public void testCommitFakeSnapshot() {
202         ReplicatedLog log = ReplicatedLogImpl.newInstance(context);
203
204         log.append(new SimpleReplicatedLogEntry(0, 1, new MockPayload("0")));
205         final int dataSizeAfterFirstPayload = log.dataSize();
206
207         log.snapshotPreCommit(0,1);
208         log.snapshotCommit(false);
209
210         assertEquals(0, log.size());
211         assertEquals(dataSizeAfterFirstPayload, log.dataSize());
212     }
213
214     private static ArgumentMatcher<DeleteEntries> match(final DeleteEntries actual) {
215         return other -> actual.getFromIndex() == other.getFromIndex();
216     }
217 }