package org.corfudb.runtime.view; import lombok.Getter; import org.corfudb.protocols.wireprotocol.TokenResponse; import org.corfudb.runtime.CorfuRuntime; import org.corfudb.runtime.view.stream.IStreamView; import org.junit.Before; import org.junit.Test; import java.util.Collections; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; /** * Created by mwei on 1/8/16. */ public class StreamViewTest extends AbstractViewTest { @Getter final String defaultConfigurationString = getDefaultEndpoint(); public CorfuRuntime r; @Before public void setRuntime() throws Exception { r = getDefaultRuntime().connect(); } @Test @SuppressWarnings("unchecked") public void canReadWriteFromStream() throws Exception { UUID streamA = UUID.nameUUIDFromBytes("stream A".getBytes()); byte[] testPayload = "hello world".getBytes(); IStreamView sv = r.getStreamsView().get(streamA); sv.append(testPayload); assertThat(sv.next().getPayload(getRuntime())) .isEqualTo("hello world".getBytes()); assertThat(sv.next()) .isEqualTo(null); } @Test @SuppressWarnings("unchecked") public void canReadWriteFromStreamConcurrent() throws Exception { UUID streamA = UUID.nameUUIDFromBytes("stream A".getBytes()); byte[] testPayload = "hello world".getBytes(); IStreamView sv = r.getStreamsView().get(streamA); scheduleConcurrently(PARAMETERS.NUM_ITERATIONS_LOW, i -> sv.append(testPayload)); executeScheduled(PARAMETERS.CONCURRENCY_SOME, PARAMETERS.TIMEOUT_NORMAL); scheduleConcurrently(PARAMETERS.NUM_ITERATIONS_LOW, i -> assertThat(sv.next().getPayload(getRuntime())) .isEqualTo("hello world".getBytes())); executeScheduled(PARAMETERS.CONCURRENCY_SOME, PARAMETERS.TIMEOUT_NORMAL); assertThat(sv.next()) .isEqualTo(null); } @Test @SuppressWarnings("unchecked") public void canReadWriteFromStreamWithoutBackpointers() throws Exception { r.setBackpointersDisabled(true); UUID streamA = UUID.nameUUIDFromBytes("stream A".getBytes()); byte[] testPayload = "hello world".getBytes(); IStreamView sv = r.getStreamsView().get(streamA); scheduleConcurrently(PARAMETERS.NUM_ITERATIONS_LOW, i -> sv.append(testPayload)); executeScheduled(PARAMETERS.CONCURRENCY_SOME, PARAMETERS.TIMEOUT_NORMAL); scheduleConcurrently(PARAMETERS.NUM_ITERATIONS_LOW, i -> assertThat(sv.next().getPayload(getRuntime())) .isEqualTo("hello world".getBytes())); executeScheduled(PARAMETERS.CONCURRENCY_SOME, PARAMETERS.TIMEOUT_NORMAL); assertThat(sv.next()) .isEqualTo(null); } @Test @SuppressWarnings("unchecked") public void canReadWriteFromCachedStream() throws Exception { CorfuRuntime r = getDefaultRuntime().connect() .setCacheDisabled(false); UUID streamA = UUID.nameUUIDFromBytes("stream A".getBytes()); byte[] testPayload = "hello world".getBytes(); IStreamView sv = r.getStreamsView().get(streamA); sv.append(testPayload); assertThat(sv.next().getPayload(getRuntime())) .isEqualTo("hello world".getBytes()); assertThat(sv.next()) .isEqualTo(null); } @Test public void canSeekOnStream() throws Exception { CorfuRuntime r = getDefaultRuntime().connect(); IStreamView sv = r.getStreamsView().get( CorfuRuntime.getStreamID("stream A")); // Append some entries sv.append("a".getBytes()); sv.append("b".getBytes()); sv.append("c".getBytes()); // Try reading two entries assertThat(sv.next().getPayload(r)) .isEqualTo("a".getBytes()); assertThat(sv.next().getPayload(r)) .isEqualTo("b".getBytes()); // Seeking to the beginning sv.seek(0); assertThat(sv.next().getPayload(r)) .isEqualTo("a".getBytes()); // Seeking to the end sv.seek(2); assertThat(sv.next().getPayload(r)) .isEqualTo("c".getBytes()); // Seeking to the middle sv.seek(1); assertThat(sv.next().getPayload(r)) .isEqualTo("b".getBytes()); } @Test public void canFindInStream() throws Exception { CorfuRuntime r = getDefaultRuntime().connect(); IStreamView svA = r.getStreamsView().get( CorfuRuntime.getStreamID("stream A")); IStreamView svB = r.getStreamsView().get( CorfuRuntime.getStreamID("stream B")); // Append some entries final long A_GLOBAL = 0; svA.append("a".getBytes()); final long B_GLOBAL = 1; svB.append("b".getBytes()); final long C_GLOBAL = 2; svA.append("c".getBytes()); final long D_GLOBAL = 3; svB.append("d".getBytes()); final long E_GLOBAL = 4; svA.append("e".getBytes()); // See if we can find entries: // Should find entry "c" assertThat(svA.find(B_GLOBAL, IStreamView.SearchDirection.FORWARD)) .isEqualTo(C_GLOBAL); // Should find entry "a" assertThat(svA.find(B_GLOBAL, IStreamView.SearchDirection.REVERSE)) .isEqualTo(A_GLOBAL); // Should find entry "e" assertThat(svA.find(E_GLOBAL, IStreamView.SearchDirection.FORWARD_INCLUSIVE)) .isEqualTo(E_GLOBAL); // Should find entry "c" assertThat(svA.find(C_GLOBAL, IStreamView.SearchDirection.REVERSE_INCLUSIVE)) .isEqualTo(C_GLOBAL); // From existing to existing: // Should find entry "b" assertThat(svB.find(D_GLOBAL, IStreamView.SearchDirection.REVERSE)) .isEqualTo(B_GLOBAL); // Should find entry "d" assertThat(svB.find(B_GLOBAL, IStreamView.SearchDirection.FORWARD)) .isEqualTo(D_GLOBAL); // Bounds: assertThat(svB.find(D_GLOBAL, IStreamView.SearchDirection.FORWARD)) .isEqualTo(Address.NOT_FOUND); } @Test public void canDoPreviousOnStream() throws Exception { CorfuRuntime r = getDefaultRuntime().connect(); IStreamView sv = r.getStreamsView().get( CorfuRuntime.getStreamID("stream A")); // Append some entries sv.append("a".getBytes()); sv.append("b".getBytes()); sv.append("c".getBytes()); // Move backward should return null assertThat(sv.previous()) .isNull(); // Move forward sv.next(); // "a" sv.next(); // "b" // Should be now "a" assertThat(sv.previous().getPayload(r)) .isEqualTo("a".getBytes()); // Move forward, should be now "b" assertThat(sv.next().getPayload(r)) .isEqualTo("b".getBytes()); sv.next(); // "c" sv.next(); // null // Should be now "b" assertThat(sv.previous().getPayload(r)) .isEqualTo("b".getBytes()); } @Test @SuppressWarnings("unchecked") public void streamCanSurviveOverwriteException() throws Exception { UUID streamA = CorfuRuntime.getStreamID("stream A"); byte[] testPayload = "hello world".getBytes(); // read from an address that hasn't been written to // causing a hole fill r.getAddressSpaceView().read(0L); // Write to the stream, and read back. The hole should be filled. IStreamView sv = r.getStreamsView().get(streamA); sv.append(testPayload); assertThat(sv.next().getPayload(getRuntime())) .isEqualTo("hello world".getBytes()); assertThat(sv.next()) .isEqualTo(null); } @Test @SuppressWarnings("unchecked") public void streamWillHoleFill() throws Exception { //begin tests UUID streamA = CorfuRuntime.getStreamID("stream A"); byte[] testPayload = "hello world".getBytes(); // Generate a hole. r.getSequencerView().nextToken(Collections.singleton(streamA), 1); // Write to the stream, and read back. The hole should be filled. IStreamView sv = r.getStreamsView().get(streamA); sv.append(testPayload); assertThat(sv.next().getPayload(getRuntime())) .isEqualTo("hello world".getBytes()); assertThat(sv.next()) .isEqualTo(null); } @Test @SuppressWarnings("unchecked") public void streamWithHoleFill() throws Exception { UUID streamA = CorfuRuntime.getStreamID("stream A"); byte[] testPayload = "hello world".getBytes(); byte[] testPayload2 = "hello world2".getBytes(); IStreamView sv = r.getStreamsView().get(streamA); sv.append(testPayload); //generate a stream hole TokenResponse tr = r.getSequencerView().nextToken(Collections.singleton(streamA), 1); // read from an address that hasn't been written to // causing a hole fill r.getAddressSpaceView().read(tr.getToken().getTokenValue()); tr = r.getSequencerView().nextToken(Collections.singleton(streamA), 1); // read from an address that hasn't been written to // causing a hole fill r.getAddressSpaceView().read(tr.getToken().getTokenValue()); sv.append(testPayload2); //make sure we can still read the stream. assertThat(sv.next().getPayload(getRuntime())) .isEqualTo(testPayload); assertThat(sv.next().getPayload(getRuntime())) .isEqualTo(testPayload2); } }