private static void assertCodeEquals(io.grpc.Status expected, io.grpc.Status actual) { if (expected == null) { fail("expected should not be null"); } if (actual == null || !expected.getCode().equals(actual.getCode())) { assertEquals(expected, actual); } }
@Test public void cancelInOnMessageShouldInvokeStreamCancel() throws Exception { final ClientCallImpl<Void, Void> call = new ClientCallImpl<Void, Void>( DESCRIPTOR, MoreExecutors.directExecutor(), CallOptions.DEFAULT, provider, deadlineCancellationExecutor); final Exception cause = new Exception(); ClientCall.Listener<Void> callListener = new ClientCall.Listener<Void>() { @Override public void onMessage(Void message) { call.cancel("foo", cause); } }; call.start(callListener, new Metadata()); call.halfClose(); call.request(1); verify(stream).start(listenerArgumentCaptor.capture()); ClientStreamListener streamListener = listenerArgumentCaptor.getValue(); streamListener.onReady(); streamListener.headersRead(new Metadata()); streamListener.messageRead(new ByteArrayInputStream(new byte[0])); verify(stream).cancel(statusCaptor.capture()); Status status = statusCaptor.getValue(); assertEquals(Status.CANCELLED.getCode(), status.getCode()); assertEquals("foo", status.getDescription()); assertSame(cause, status.getCause()); }
@Override public void closed(Status status, Metadata trailers) { Deadline deadline = context.getDeadline(); if (status.getCode() == Status.Code.CANCELLED && deadline != null) { // When the server's deadline expires, it can only reset the stream with CANCEL and no // description. Since our timer may be delayed in firing, we double-check the deadline and // turn the failure into the likely more helpful DEADLINE_EXCEEDED status. if (deadline.isExpired()) { status = DEADLINE_EXCEEDED; // Replace trailers to prevent mixing sources of status and trailers. trailers = new Metadata(); } } final Status savedStatus = status; final Metadata savedTrailers = trailers; callExecutor.execute( new ContextRunnable(context) { @Override public final void runInContext() { try { closed = true; contextListenerShouldBeRemoved = true; observer.onClose(savedStatus, savedTrailers); } finally { context.removeListener(ClientCallImpl.this); } } }); }
@Test public void statusFromCancelled_returnCancelledIfCauseIsNull() { Context.CancellableContext cancellableContext = Context.current().withCancellation(); cancellableContext.cancel(null); assertTrue(cancellableContext.isCancelled()); Status status = statusFromCancelled(cancellableContext); assertNotNull(status); assertEquals(Status.Code.CANCELLED, status.getCode()); }
@Test public void statusFromCancelled_returnStatusAsSetOnCtx() { Context.CancellableContext cancellableContext = Context.current().withCancellation(); cancellableContext.cancel(Status.DEADLINE_EXCEEDED.withDescription("foo bar").asException()); Status status = statusFromCancelled(cancellableContext); assertNotNull(status); assertEquals(Status.Code.DEADLINE_EXCEEDED, status.getCode()); assertEquals("foo bar", status.getDescription()); }
@Test public void statusFromCancelled_shouldReturnStatusWithCauseAttached() { Context.CancellableContext cancellableContext = Context.current().withCancellation(); Throwable t = new Throwable(); cancellableContext.cancel(t); Status status = statusFromCancelled(cancellableContext); assertNotNull(status); assertEquals(Status.Code.CANCELLED, status.getCode()); assertSame(t, status.getCause()); }
static { TreeMap<Integer, Status> canonicalizer = new TreeMap<Integer, Status>(); for (Code code : Code.values()) { Status replaced = canonicalizer.put(code.value(), new Status(code)); if (replaced != null) { throw new IllegalStateException( "Code value duplication between " + replaced.getCode().name() + " & " + code.name()); } } STATUS_LIST = new ArrayList<Status>(canonicalizer.values()); }
/** This is a whitebox test, to verify a special case of the implementation. */ @Test public void statusFromCancelled_StatusUnknownShouldWork() { Context.CancellableContext cancellableContext = Context.current().withCancellation(); Exception e = Status.UNKNOWN.asException(); cancellableContext.cancel(e); assertTrue(cancellableContext.isCancelled()); Status status = statusFromCancelled(cancellableContext); assertNotNull(status); assertEquals(Status.Code.UNKNOWN, status.getCode()); assertSame(e, status.getCause()); }
@Test public void contextAlreadyCancelledNotifiesImmediately() throws Exception { // Attach the context which is recorded when the call is created Context.CancellableContext cancellableContext = Context.current().withCancellation(); Throwable cause = new Throwable(); cancellableContext.cancel(cause); Context previous = cancellableContext.attach(); ClientCallImpl<Void, Void> call = new ClientCallImpl<Void, Void>( DESCRIPTOR, new SerializingExecutor(Executors.newSingleThreadExecutor()), CallOptions.DEFAULT, provider, deadlineCancellationExecutor) .setDecompressorRegistry(decompressorRegistry); previous.attach(); final SettableFuture<Status> statusFuture = SettableFuture.create(); call.start( new ClientCall.Listener<Void>() { @Override public void onClose(Status status, Metadata trailers) { statusFuture.set(status); } }, new Metadata()); // Caller should receive onClose callback. Status status = statusFuture.get(5, TimeUnit.SECONDS); assertEquals(Status.Code.CANCELLED, status.getCode()); assertSame(cause, status.getCause()); // Following operations should be no-op. call.request(1); call.sendMessage(null); call.halfClose(); // Stream should never be created. verifyZeroInteractions(transport); try { call.sendMessage(null); fail("Call has been cancelled"); } catch (IllegalStateException ise) { // expected } }
@Test public void statusFromCancelled_TimeoutExceptionShouldMapToDeadlineExceeded() { FakeClock fakeClock = new FakeClock(); Context.CancellableContext cancellableContext = Context.current() .withDeadlineAfter(100, TimeUnit.MILLISECONDS, fakeClock.scheduledExecutorService); fakeClock.forwardTime(System.nanoTime(), TimeUnit.NANOSECONDS); fakeClock.forwardMillis(100); assertTrue(cancellableContext.isCancelled()); assertThat(cancellableContext.cancellationCause(), instanceOf(TimeoutException.class)); Status status = statusFromCancelled(cancellableContext); assertNotNull(status); assertEquals(Status.Code.DEADLINE_EXCEEDED, status.getCode()); assertEquals("context timed out", status.getDescription()); }
@Override public String toAsciiString(Status status) { return status.getCode().valueAscii(); }