mirror of https://github.com/grpc/grpc-java.git
Add unit tests for AbstractServerStream
This commit is contained in:
parent
0958fd407e
commit
d678498ca7
|
|
@ -88,7 +88,7 @@ public abstract class AbstractServerStream<IdT> extends AbstractStream<IdT>
|
||||||
@Override
|
@Override
|
||||||
protected void receiveMessage(InputStream is) {
|
protected void receiveMessage(InputStream is) {
|
||||||
inboundPhase(Phase.MESSAGE);
|
inboundPhase(Phase.MESSAGE);
|
||||||
listener.messageRead(is);
|
listener().messageRead(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -115,7 +115,7 @@ public abstract class AbstractServerStream<IdT> extends AbstractStream<IdT>
|
||||||
Preconditions.checkNotNull(trailers, "trailers");
|
Preconditions.checkNotNull(trailers, "trailers");
|
||||||
if (outboundPhase(Phase.STATUS) != Phase.STATUS) {
|
if (outboundPhase(Phase.STATUS) != Phase.STATUS) {
|
||||||
gracefulClose = true;
|
gracefulClose = true;
|
||||||
this.stashedTrailers = trailers;
|
stashedTrailers = trailers;
|
||||||
writeStatusToTrailers(status);
|
writeStatusToTrailers(status);
|
||||||
closeFramer();
|
closeFramer();
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +253,7 @@ public abstract class AbstractServerStream<IdT> extends AbstractStream<IdT>
|
||||||
private void halfCloseListener() {
|
private void halfCloseListener() {
|
||||||
if (inboundPhase(Phase.STATUS) != Phase.STATUS && !listenerClosed) {
|
if (inboundPhase(Phase.STATUS) != Phase.STATUS && !listenerClosed) {
|
||||||
closeDeframer();
|
closeDeframer();
|
||||||
listener.halfClosed();
|
listener().halfClosed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -264,7 +264,7 @@ public abstract class AbstractServerStream<IdT> extends AbstractStream<IdT>
|
||||||
if (!listenerClosed) {
|
if (!listenerClosed) {
|
||||||
listenerClosed = true;
|
listenerClosed = true;
|
||||||
closeDeframer();
|
closeDeframer();
|
||||||
listener.closed(newStatus);
|
listener().closed(newStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,331 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015, Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.grpc.internal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Matchers.isA;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import io.grpc.Metadata;
|
||||||
|
import io.grpc.Status;
|
||||||
|
import io.grpc.internal.AbstractStream.Phase;
|
||||||
|
import io.grpc.internal.MessageFramerTest.ByteWritableBuffer;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AbstractServerStream}.
|
||||||
|
*/
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class AbstractServerStreamTest {
|
||||||
|
private static int MAX_MESSAGE_SIZE = 100;
|
||||||
|
|
||||||
|
@Rule public final ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
|
private final WritableBufferAllocator allocator = new WritableBufferAllocator() {
|
||||||
|
@Override
|
||||||
|
public WritableBuffer allocate(int capacityHint) {
|
||||||
|
return new ByteWritableBuffer(capacityHint);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final AbstractServerStreamBase defaultStream =
|
||||||
|
new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setListener_setOnlyOnce() {
|
||||||
|
|
||||||
|
defaultStream.setListener(new ServerStreamListenerBase());
|
||||||
|
thrown.expect(IllegalStateException.class);
|
||||||
|
|
||||||
|
defaultStream.setListener(new ServerStreamListenerBase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setListener_readyCalled() {
|
||||||
|
ServerStreamListener streamListener = mock(ServerStreamListener.class);
|
||||||
|
defaultStream.setListener(streamListener);
|
||||||
|
|
||||||
|
verify(streamListener).onReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setListener_failsOnNull() {
|
||||||
|
thrown.expect(NullPointerException.class);
|
||||||
|
|
||||||
|
defaultStream.setListener(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void receiveMessage_listenerCalled() {
|
||||||
|
final ServerStreamListener streamListener = mock(ServerStreamListener.class);
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected ServerStreamListener listener() {
|
||||||
|
return streamListener;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normally called by a deframe event.
|
||||||
|
stream.receiveMessage(new ByteArrayInputStream(new byte[]{}));
|
||||||
|
|
||||||
|
verify(streamListener).messageRead(isA(InputStream.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void receiveMessage_failsIfHalfClosed() {
|
||||||
|
// Simulate being closed, without invoking the listener
|
||||||
|
defaultStream.inboundPhase(Phase.STATUS);
|
||||||
|
|
||||||
|
thrown.expect(IllegalStateException.class);
|
||||||
|
|
||||||
|
// Normally called by a deframe event.
|
||||||
|
defaultStream.receiveMessage(new ByteArrayInputStream(new byte[]{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeHeaders_failsOnNullHeaders() {
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE);
|
||||||
|
thrown.expect(NullPointerException.class);
|
||||||
|
|
||||||
|
stream.writeHeaders(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeHeaders_failsIfAlreadySent() {
|
||||||
|
defaultStream.writeHeaders(new Metadata());
|
||||||
|
thrown.expect(IllegalStateException.class);
|
||||||
|
|
||||||
|
defaultStream.writeHeaders(new Metadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeHeaders() {
|
||||||
|
final AtomicReference<Metadata> capturedHeaders = new AtomicReference<Metadata>(null);
|
||||||
|
Metadata headers = new Metadata();
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void internalSendHeaders(Metadata captured) {
|
||||||
|
capturedHeaders.set(captured);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.writeHeaders(headers);
|
||||||
|
|
||||||
|
assertEquals(headers, capturedHeaders.get());
|
||||||
|
assertEquals(Phase.MESSAGE, stream.outboundPhase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeMessage_writeHeadersIfNeeded() {
|
||||||
|
final AtomicReference<Metadata> capturedHeaders = new AtomicReference<Metadata>(null);
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void internalSendHeaders(Metadata captured) {
|
||||||
|
capturedHeaders.set(captured);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.writeMessage(new ByteArrayInputStream(new byte[]{}));
|
||||||
|
|
||||||
|
assertNotNull(capturedHeaders.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeMessage_dontWriteDuplicateHeaders() {
|
||||||
|
final AtomicReference<Metadata> capturedHeaders = new AtomicReference<Metadata>(null);
|
||||||
|
Metadata headers = new Metadata();
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void internalSendHeaders(Metadata captured) {
|
||||||
|
capturedHeaders.set(captured);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stream.writeHeaders(headers);
|
||||||
|
|
||||||
|
stream.writeMessage(new ByteArrayInputStream(new byte[]{}));
|
||||||
|
|
||||||
|
// Make sure it wasn't called twice, by checking that the exact headers sent are the ones
|
||||||
|
// returned.
|
||||||
|
assertSame(headers, capturedHeaders.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeMessage_ignoreIfFramerClosed() {
|
||||||
|
final AtomicBoolean sendCalled = new AtomicBoolean();
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void sendFrame(WritableBuffer frame, boolean endOfStream, boolean flush) {
|
||||||
|
sendCalled.set(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stream.closeFramer();
|
||||||
|
|
||||||
|
stream.writeMessage(new ByteArrayInputStream(new byte[]{}));
|
||||||
|
|
||||||
|
assertFalse(sendCalled.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeMessage() {
|
||||||
|
final AtomicBoolean sendCalled = new AtomicBoolean();
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void sendFrame(WritableBuffer frame, boolean endOfStream, boolean flush) {
|
||||||
|
sendCalled.set(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.writeMessage(new ByteArrayInputStream(new byte[]{}));
|
||||||
|
// Force the message to be flushed
|
||||||
|
stream.closeFramer();
|
||||||
|
|
||||||
|
assertTrue(sendCalled.get());
|
||||||
|
assertEquals(Phase.MESSAGE, stream.outboundPhase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void close_failsOnNullStatus() {
|
||||||
|
thrown.expect(NullPointerException.class);
|
||||||
|
|
||||||
|
defaultStream.close(null, new Metadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void close_failsOnNullMetadata() {
|
||||||
|
thrown.expect(NullPointerException.class);
|
||||||
|
|
||||||
|
defaultStream.close(Status.INTERNAL, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void close_sendsTrailers() {
|
||||||
|
final AtomicReference<Metadata> capturedTrailers = new AtomicReference<Metadata>(null);
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void sendTrailers(Metadata trailers, boolean headersSent) {
|
||||||
|
capturedTrailers.set(trailers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Metadata trailers = new Metadata();
|
||||||
|
|
||||||
|
stream.close(Status.INTERNAL, trailers);
|
||||||
|
|
||||||
|
assertSame(trailers, capturedTrailers.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void close_sendTrailersClearsReservedFields() {
|
||||||
|
final AtomicReference<Metadata> capturedTrailers = new AtomicReference<Metadata>(null);
|
||||||
|
AbstractServerStreamBase stream = new AbstractServerStreamBase(allocator, MAX_MESSAGE_SIZE) {
|
||||||
|
@Override
|
||||||
|
protected void sendTrailers(Metadata trailers, boolean headersSent) {
|
||||||
|
capturedTrailers.set(trailers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// stream actually mutates trailers, so we can't check that the fields here are the same as
|
||||||
|
// the captured ones.
|
||||||
|
Metadata trailers = new Metadata();
|
||||||
|
trailers.put(Status.CODE_KEY, Status.OK);
|
||||||
|
trailers.put(Status.MESSAGE_KEY, "Everything's super.");
|
||||||
|
|
||||||
|
stream.close(Status.INTERNAL.withDescription("bad"), trailers);
|
||||||
|
|
||||||
|
assertEquals(Status.Code.INTERNAL, capturedTrailers.get().get(Status.CODE_KEY).getCode());
|
||||||
|
assertEquals("bad", capturedTrailers.get().get(Status.MESSAGE_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ServerStreamListenerBase implements ServerStreamListener {
|
||||||
|
@Override
|
||||||
|
public void messageRead(InputStream message) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReady() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void halfClosed() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closed(Status status) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AbstractServerStreamBase extends AbstractServerStream<Void> {
|
||||||
|
protected AbstractServerStreamBase(
|
||||||
|
WritableBufferAllocator bufferAllocator, int maxMessageSize) {
|
||||||
|
super(bufferAllocator, maxMessageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(Status status) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void request(int numMessages) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void internalSendHeaders(Metadata headers) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendFrame(WritableBuffer frame, boolean endOfStream, boolean flush) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendTrailers(Metadata trailers, boolean headersSent) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Void id() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inboundDeliveryPaused() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void returnProcessedBytes(int processedBytes) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue