/*
* Copyright (c) 2011-2016 Pivotal Software Inc, All Rights Reserved.
*
* Licensed 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 reactor.core.publisher;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.reactivestreams.Subscription;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class LambdaSubscriberTest {
@Test
public void consumeOnSubscriptionNotifiesError() {
AtomicReference<Throwable> errorHolder = new AtomicReference<>(null);
LambdaSubscriber<String> tested = new LambdaSubscriber<>(
value -> {},
errorHolder::set,
() -> {},
subscription -> { throw new IllegalArgumentException(); });
TestSubscription testSubscription = new TestSubscription();
//the error is expected to be propagated through onError
tested.onSubscribe(testSubscription);
assertThat("unexpected exception in onError",
errorHolder.get(), is(instanceOf(IllegalArgumentException.class)));
assertThat("subscription has not been cancelled",
testSubscription.isCancelled, is(true));
assertThat("unexpected request",
testSubscription.requested, is(equalTo(-1L)));
}
@Test
public void consumeOnSubscriptionThrowsFatal() {
AtomicReference<Throwable> errorHolder = new AtomicReference<>(null);
LambdaSubscriber<String> tested = new LambdaSubscriber<>(
value -> {},
errorHolder::set,
() -> {},
subscription -> { throw new OutOfMemoryError(); });
TestSubscription testSubscription = new TestSubscription();
//the error is expected to be thrown as it is fatal
try {
tested.onSubscribe(testSubscription);
fail("Expected OutOfMemoryError to be thrown");
}
catch (OutOfMemoryError e) {
//expected
}
assertThat("unexpected onError", errorHolder.get(), is(nullValue()));
assertThat("subscription has been cancelled despite fatal exception",
testSubscription.isCancelled, is(not(true)));
assertThat("unexpected request",
testSubscription.requested, is(equalTo(-1L)));
}
@Test
public void consumeOnSubscriptionReceivesSubscriptionAndRequests32() {
AtomicReference<Throwable> errorHolder = new AtomicReference<>(null);
AtomicReference<Subscription> subscriptionHolder = new AtomicReference<>(null);
LambdaSubscriber<String> tested = new LambdaSubscriber<>(
value -> {},
errorHolder::set,
() -> { },
s -> {
subscriptionHolder.set(s);
s.request(32);
});
TestSubscription testSubscription = new TestSubscription();
tested.onSubscribe(testSubscription);
assertThat("unexpected onError", errorHolder.get(), is(nullValue()));
assertThat("subscription has been cancelled",
testSubscription.isCancelled, is(not(true)));
assertThat("didn't consume the subscription",
subscriptionHolder.get(), is(equalTo(testSubscription)));
assertThat("didn't request the subscription",
testSubscription.requested, is(equalTo(32L)));
}
@Test
public void noSubscriptionConsumerTriggersRequestOfMax() {
AtomicReference<Throwable> errorHolder = new AtomicReference<>(null);
LambdaSubscriber<String> tested = new LambdaSubscriber<>(
value -> {},
errorHolder::set,
() -> {},
null); //defaults to initial request of max
TestSubscription testSubscription = new TestSubscription();
tested.onSubscribe(testSubscription);
assertThat("unexpected onError", errorHolder.get(), is(nullValue()));
assertThat("subscription has been cancelled",
testSubscription.isCancelled, is(not(true)));
assertThat("didn't request the subscription",
testSubscription.requested, is(not(equalTo(-1L))));
assertThat("didn't request max",
testSubscription.requested, is(equalTo(Long.MAX_VALUE)));
}
@Test
public void onNextConsumerExceptionTriggersCancellation() {
AtomicReference<Throwable> errorHolder = new AtomicReference<>(null);
LambdaSubscriber<String> tested = new LambdaSubscriber<>(
value -> { throw new IllegalArgumentException(); },
errorHolder::set,
() -> {},
null);
TestSubscription testSubscription = new TestSubscription();
tested.onSubscribe(testSubscription);
//the error is expected to be propagated through onError
tested.onNext("foo");
assertThat("unexpected exception in onError",
errorHolder.get(), is(instanceOf(IllegalArgumentException.class)));
assertThat("subscription has not been cancelled",
testSubscription.isCancelled, is(true));
}
@Test
public void onNextConsumerFatalDoesntTriggerCancellation() {
AtomicReference<Throwable> errorHolder = new AtomicReference<>(null);
LambdaSubscriber<String> tested = new LambdaSubscriber<>(
value -> { throw new OutOfMemoryError(); },
errorHolder::set,
() -> {},
null);
TestSubscription testSubscription = new TestSubscription();
tested.onSubscribe(testSubscription);
//the error is expected to be thrown as it is fatal
try {
tested.onNext("foo");
fail("Expected OutOfMemoryError to be thrown");
}
catch (OutOfMemoryError e) {
//expected
}
assertThat("unexpected onError", errorHolder.get(), is(nullValue()));
assertThat("subscription has been cancelled despite fatal exception",
testSubscription.isCancelled, is(not(true)));
}
private static class TestSubscription implements Subscription {
volatile boolean isCancelled = false;
volatile long requested = -1L;
@Override
public void request(long n) {
this.requested = n;
}
@Override
public void cancel() {
this.isCancelled = true;
}
}
}