package lsr.paxos.storage;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lsr.common.DirectoryHelper;
import lsr.common.Reply;
import lsr.common.RequestId;
import lsr.paxos.Snapshot;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class FullSSDiscWriterTest {
private String directoryPath = "bin/logs";
private FullSSDiscWriter writer;
@Before
public void setUp() throws FileNotFoundException {
DirectoryHelper.create(directoryPath);
writer = new FullSSDiscWriter(directoryPath);
}
@After
public void tearDown() throws IOException {
writer.close();
DirectoryHelper.delete(directoryPath);
}
@Test
public void shouldWriteOnInstanceViewChange() throws IOException {
writer.changeInstanceView(1, 2);
ByteBuffer buffer = ByteBuffer.allocate(9);
buffer.put((byte) 1); // type
buffer.putInt(1); // id
buffer.putInt(2); // view
assertArrayEquals(buffer.array(), readFile(directoryPath + "/sync.0.log"));
}
@Test
public void shouldWriteOnInstanceValueChange() throws IOException {
byte[] value = new byte[] {1, 2};
writer.changeInstanceValue(1, 2, value);
ByteBuffer buffer = ByteBuffer.allocate(15);
buffer.put((byte) 2); // type
buffer.putInt(1); // id
buffer.putInt(2); // view
buffer.putInt(2); // value size
buffer.put(value); // value
String path = directoryPath + "/sync.0.log";
assertArrayEquals(readFile(path), buffer.array());
}
@Test
public void shouldGetNextLogNumber() throws IOException {
String[] s = new String[] {"sync.0.log", "invalid", "sync.2.log", "sync.1.log"};
int lastLogNumber = writer.getLastLogNumber(s);
assertEquals(lastLogNumber, 2);
}
@Test
public void shouldChangeViewNumber() throws IOException {
writer.changeViewNumber(5);
DataInputStream stream = new DataInputStream(new FileInputStream(directoryPath +
"/sync.0.view"));
int view = stream.readInt();
stream.close();
assertEquals(view, 5);
}
@Test
public void shouldLoad() throws IOException {
byte[] value = new byte[] {1, 2};
byte[] newValue = new byte[] {1, 2, 3};
writer.changeInstanceValue(1, 2, value);
writer.changeInstanceValue(2, 2, value);
writer.changeInstanceValue(1, 3, newValue);
writer.changeInstanceView(1, 4);
writer.close();
writer = new FullSSDiscWriter(directoryPath);
ConsensusInstance[] instances = writer.load().toArray(new ConsensusInstance[0]);
writer.close();
ConsensusInstance instance1 = instances[0];
ConsensusInstance instance2 = instances[1];
assertEquals(4, instance1.getView());
assertArrayEquals(newValue, instance1.getValue());
assertEquals(2, instance2.getView());
assertArrayEquals(value, instance2.getValue());
}
@Test
public void shouldLoadCorruptedData() throws IOException {
byte[] value = new byte[] {1, 2};
byte[] newValue = new byte[] {1, 2, 3};
writer.changeInstanceValue(1, 2, value);
writer.changeInstanceValue(2, 2, value);
writer.changeInstanceValue(1, 3, newValue);
writer.changeInstanceView(1, 4);
writer.close();
FileOutputStream stream = new FileOutputStream(directoryPath + "/sync.0.log", true);
stream.write(new byte[] {1, 2, 3});
stream.close();
writer = new FullSSDiscWriter(directoryPath);
ConsensusInstance[] instances = writer.load().toArray(new ConsensusInstance[0]);
writer.close();
ConsensusInstance instance1 = instances[0];
ConsensusInstance instance2 = instances[1];
assertEquals(4, instance1.getView());
assertArrayEquals(newValue, instance1.getValue());
assertEquals(2, instance2.getView());
assertArrayEquals(value, instance2.getValue());
}
@Test
public void shouldLoadDataFromMoreFiles() throws IOException {
byte[] value = new byte[] {1, 2};
byte[] newValue = new byte[] {1, 2, 3};
writer.changeInstanceValue(1, 2, value);
writer.changeInstanceValue(2, 2, value);
writer.changeInstanceValue(1, 3, newValue);
writer.close();
writer = new FullSSDiscWriter(directoryPath);
writer.changeInstanceView(1, 4);
writer.close();
writer = new FullSSDiscWriter(directoryPath);
ConsensusInstance[] instances = writer.load().toArray(new ConsensusInstance[0]);
writer.close();
ConsensusInstance instance1 = instances[0];
ConsensusInstance instance2 = instances[1];
assertEquals(4, instance1.getView());
assertArrayEquals(newValue, instance1.getValue());
assertEquals(2, instance2.getView());
assertArrayEquals(value, instance2.getValue());
}
@Test
public void shouldLoadViewNumber() throws IOException {
writer.changeViewNumber(5);
writer.close();
writer = new FullSSDiscWriter(directoryPath);
int view = writer.loadViewNumber();
writer.close();
assertEquals(view, 5);
}
@Test
public void shouldWriteNewSnapshot() throws IOException {
Snapshot snapshot = new Snapshot();
snapshot.setNextInstanceId(2);
snapshot.setValue(new byte[] {1, 2, 3});
Map<Long, Reply> lastReplyForClient = new HashMap<Long, Reply>();
lastReplyForClient.put((long) 1, new Reply(new RequestId(3, 1), new byte[] {1}));
snapshot.setLastReplyForClient(lastReplyForClient);
List<Reply> partialResponseCache = Arrays.asList(
new Reply(new RequestId(1, 1), new byte[] {1, 2, 3}),
new Reply(new RequestId(2, 2), new byte[] {1, 2, 3, 4}));
snapshot.setPartialResponseCache(partialResponseCache);
writer.newSnapshot(snapshot);
writer.close();
writer = new FullSSDiscWriter(directoryPath);
writer.load();
Snapshot actual = writer.getSnapshot();
assertArrayEquals(snapshot.getValue(), actual.getValue());
assertEquals(snapshot.getNextInstanceId(), actual.getNextInstanceId());
assertEquals(snapshot.getNextRequestSeqNo(), actual.getNextRequestSeqNo());
assertEquals(snapshot.getStartingRequestSeqNo(), actual.getStartingRequestSeqNo());
}
private byte[] readFile(String path) throws IOException {
FileInputStream stream = new FileInputStream(path);
int length = stream.available(); // danger
byte[] value = new byte[length];
stream.read(value);
stream.close();
return value;
}
}