/*
* Copyright 2016 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.common.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.MoreExecutors;
public class DeferredStreamMessageTest {
@Test
public void testInitialState() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
assertThat(m.isOpen()).isTrue();
assertThat(m.isEmpty()).isFalse();
assertThat(m.closeFuture()).isNotDone();
}
@Test
public void testSetDelegate() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
m.delegate(new DefaultStreamMessage<>());
assertThatThrownBy(() -> m.delegate(new DefaultStreamMessage<>()))
.isInstanceOf(IllegalStateException.class);
assertThatThrownBy(() -> m.delegate(null)).isInstanceOf(NullPointerException.class);
}
@Test
public void testEarlyAbort() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
m.abort();
assertAborted(m);
assertThatThrownBy(() -> m.subscribe(mock(Subscriber.class))).isInstanceOf(IllegalStateException.class);
}
@Test
public void testEarlyAbortWithSubscriber() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
m.subscribe(mock(Subscriber.class));
m.abort();
assertAborted(m);
final DefaultStreamMessage<Object> d = new DefaultStreamMessage<>();
m.delegate(d);
assertAborted(d);
}
@Test
public void testLateAbort() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
final DefaultStreamMessage<Object> d = new DefaultStreamMessage<>();
m.delegate(d);
m.abort();
assertAborted(m);
assertAborted(d);
}
@Test
public void testLateAbortWithSubscriber() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
final DefaultStreamMessage<Object> d = new DefaultStreamMessage<>();
@SuppressWarnings("unchecked")
final Subscriber<Object> subscriber = mock(Subscriber.class);
m.subscribe(subscriber);
m.delegate(d);
verify(subscriber).onSubscribe(any());
m.abort();
verify(subscriber, never()).onError(any());
assertAborted(m);
assertAborted(d);
}
@Test
public void testEarlySubscription() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
final DefaultStreamMessage<Object> d = new DefaultStreamMessage<>();
@SuppressWarnings("unchecked")
final Subscriber<Object> subscriber = mock(Subscriber.class);
m.subscribe(subscriber);
assertThatThrownBy(() -> m.subscribe(mock(Subscriber.class))).isInstanceOf(IllegalStateException.class);
m.delegate(d);
verify(subscriber).onSubscribe(any());
}
@Test
public void testLateSubscription() throws Exception {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
final DefaultStreamMessage<Object> d = new DefaultStreamMessage<>();
m.delegate(d);
@SuppressWarnings("unchecked")
final Subscriber<Object> subscriber = mock(Subscriber.class);
m.subscribe(subscriber);
verify(subscriber).onSubscribe(any());
assertThatThrownBy(() -> m.subscribe(mock(Subscriber.class))).isInstanceOf(IllegalStateException.class);
}
private static void assertAborted(StreamMessage<?> m) {
assertThat(m.isOpen()).isFalse();
assertThat(m.isEmpty()).isTrue();
assertThat(m.closeFuture()).isCompletedExceptionally();
assertThatThrownBy(() -> m.closeFuture().get())
.hasCauseInstanceOf(CancelledSubscriptionException.class);
}
@Test
public void testStreamingWithoutExecutor() throws Exception {
testStreaming(false);
}
@Test
public void testStreamingWithExecutor() throws Exception {
testStreaming(true);
}
private void testStreaming(boolean useExecutor) {
final DeferredStreamMessage<Object> m = new DeferredStreamMessage<>();
final DefaultStreamMessage<Object> d = new DefaultStreamMessage<>();
m.delegate(d);
final List<Object> streamed = new ArrayList<>();
final Subscriber<Object> subscriber = new Subscriber<Object>() {
@Override
public void onSubscribe(Subscription s) {
streamed.add("onSubscribe");
s.request(1);
}
@Override
public void onNext(Object o) {
streamed.add(o);
}
@Override
public void onError(Throwable t) {
streamed.add("onError: " + Throwables.getStackTraceAsString(t));
}
@Override
public void onComplete() {
streamed.add("onComplete");
}
};
if (useExecutor) {
m.subscribe(subscriber, MoreExecutors.directExecutor());
} else {
m.subscribe(subscriber);
}
assertThat(streamed).containsExactly("onSubscribe");
d.write("A");
assertThat(streamed).containsExactly("onSubscribe", "A");
d.close();
assertThat(streamed).containsExactly("onSubscribe", "A", "onComplete");
assertThat(m.isOpen()).isFalse();
assertThat(m.isEmpty()).isFalse();
assertThat(m.closeFuture()).isCompletedWithValue(null);
assertThat(d.isOpen()).isFalse();
assertThat(d.isEmpty()).isFalse();
assertThat(d.closeFuture()).isCompletedWithValue(null);
}
}