package lsr.paxos.storage; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import lsr.common.ProcessDescriptorHelper; import lsr.paxos.storage.ConsensusInstance.LogEntryState; import org.junit.Before; import org.junit.Test; public class LogTest { private Log log; @Before public void setUp() { ProcessDescriptorHelper.initialize(3, 0); log = new Log(); } @Test public void shouldBeEmptyAfterCreation() { assertEquals(0, log.getInstanceMap().size()); assertEquals(0, log.getNextId()); assertEquals(0, log.getLowestAvailableId()); } @Test public void shouldCreateEmptyInstancesAfterGetInstanceMethod() { ConsensusInstance instance = log.getInstance(2); assertEquals(3, log.getNextId()); assertEquals(3, log.getInstanceMap().size()); assertEquals(2, instance.getId()); assertEquals(-1, instance.getView()); assertEquals(null, instance.getValue()); assertEquals(LogEntryState.UNKNOWN, instance.getState()); } @Test public void shouldAppendNewInstance() { log.getInstance(2); ConsensusInstance instance = log.append(11, new byte[] {1, 2, 3}); assertEquals(4, log.getNextId()); assertEquals(4, log.getInstanceMap().size()); assertEquals(3, instance.getId()); assertEquals(11, instance.getView()); assertArrayEquals(new byte[] {1, 2, 3}, instance.getValue()); assertEquals(LogEntryState.KNOWN, instance.getState()); } @Test public void shouldNotifyListenersAboutLogSizeChange() { LogListener listener = mock(LogListener.class); log.addLogListener(listener); log.getInstance(3); verify(listener).logSizeChanged(4); log.append(3, new byte[] {1, 2, 3}); verify(listener).logSizeChanged(5); log.removeLogListener(listener); log.append(4, new byte[] {1, 2, 3}); verify(listener, never()).logSizeChanged(6); } @Test public void shouldReturnSizeOfTheLog() { log.getInstance(3); assertEquals(4, log.size()); } @Test public void shouldTruncateInstancesBelow() { // create instances [0, 10] log.getInstance(10); // remove instances [0, 4] from log log.truncateBelow(5); for (int i = 0; i < 4; i++) { assertEquals(null, log.getInstance(i)); } assertEquals(6, log.size()); assertEquals(5, log.getLowestAvailableId()); } @Test public void shouldTruncateInstancesBelowWhenLogIsEmpty() { // remove instances [0, 4] from log log.truncateBelow(5); for (int i = 0; i < 4; i++) { assertEquals(null, log.getInstance(i)); } assertEquals(0, log.size()); assertEquals(5, log.getLowestAvailableId()); assertEquals(5, log.getNextId()); } @Test public void shouldTruncateAllInstances() { // create instances [0, 10] log.getInstance(10); // remove instances [0, 11] from log log.truncateBelow(12); for (int i = 0; i < 11; i++) { assertEquals(null, log.getInstance(i)); } assertEquals(0, log.size()); assertEquals(12, log.getLowestAvailableId()); assertEquals(12, log.getNextId()); } @Test public void shouldClearUndecidedInstances() { // create instances [0, 9] for (int i = 0; i < 10; i++) { log.append(5, new byte[] {1, 2, 3}); } log.getInstance(1).setDecided(); log.getInstance(3).setDecided(); log.getInstance(5).setDecided(); log.getInstance(6).setDecided(); log.getInstance(7).setDecided(); // remove instances [0, 2, 4, 8] log.clearUndecidedBelow(9); assertEquals(6, log.size()); // TODO TZ - instance 1 is lowest available assertEquals(9, log.getLowestAvailableId()); assertEquals(10, log.getNextId()); assertNull(log.getInstance(0)); assertNull(log.getInstance(2)); assertNull(log.getInstance(4)); assertNull(log.getInstance(8)); assertNotNull(log.getInstance(1)); } @Test public void shouldClearUndecidedInstancesWhenLogIsEmpty() { log.clearUndecidedBelow(5); assertEquals(0, log.size()); // TODO TZ - should it update lowest available and next id? assertEquals(0, log.getLowestAvailableId()); assertEquals(0, log.getNextId()); } @Test public void shouldCalculateSizeBetweenTwoInstances() { for (int i = 0; i < 10; i++) { log.append(4, new byte[] {1, 2, 3, 4, 5}); } long size = log.byteSizeBetween(3, 7); long expectedSize = log.getInstance(3).byteSize() + log.getInstance(4).byteSize() + log.getInstance(5).byteSize() + log.getInstance(6).byteSize(); assertEquals(expectedSize, size); } @Test public void shouldCalculateSizeBetweenTwoInstanceIfSomeAreRemoved() { // create instances [0, 9] for (int i = 0; i < 10; i++) { log.append(5, new byte[] {1, 2, 3}); } log.getInstance(1).setDecided(); log.getInstance(3).setDecided(); log.getInstance(5).setDecided(); log.getInstance(6).setDecided(); log.getInstance(7).setDecided(); // remove instances [0, 2, 4, 8] log.clearUndecidedBelow(9); long size = log.byteSizeBetween(3, 7); long expectedSize = log.getInstance(3).byteSize() + log.getInstance(5).byteSize() + log.getInstance(6).byteSize(); assertEquals(expectedSize, size); } }