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