/*
* Copyright 2016 Netflix, Inc.
*
* 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 io.reactivex.netty.client.loadbalancer;
import io.netty.buffer.ByteBuf;
import io.reactivex.netty.channel.Connection;
import io.reactivex.netty.client.ConnectionProvider;
import io.reactivex.netty.client.Host;
import io.reactivex.netty.client.HostConnector;
import io.reactivex.netty.client.events.ClientEventListener;
import io.reactivex.netty.events.EventPublisher;
import io.reactivex.netty.test.util.MockEventPublisher;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import rx.Observable;
import rx.observers.TestSubscriber;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AbstractP2CStrategyTest {
public static final int UNUSABLE_HOST_WEIGHT = -1;
@Rule
public final StrategyRule rule = new StrategyRule();
@Test
public void testNoHosts() {
ConnectionProvider<ByteBuf, ByteBuf> cp = rule.strategy.newStrategy(
Collections.<HostHolder<ByteBuf, ByteBuf>>emptyList());
TestSubscriber<Connection<ByteBuf, ByteBuf>> sub = new TestSubscriber<>();
cp.newConnectionRequest().subscribe(sub);
sub.awaitTerminalEvent();
sub.assertError(NoHostsAvailableException.class);
Assert.assertEquals("Unexpected number of hosts in the pool.", 0, rule.strategy.hostsInPool);
Assert.assertEquals("Unexpected number of no usable hosts count.", 1, rule.strategy.allUnusable);
}
@Test
public void testSingleUnusableHost() {
ConnectionProvider<ByteBuf, ByteBuf> cp = rule.strategy.newStrategy(rule.newHostStream(UNUSABLE_HOST_WEIGHT));
TestSubscriber<Connection<ByteBuf, ByteBuf>> sub = new TestSubscriber<>();
cp.newConnectionRequest().subscribe(sub);
sub.awaitTerminalEvent();
sub.assertError(NoHostsAvailableException.class);
Assert.assertEquals("Unexpected number of hosts in the pool.", 1, rule.strategy.hostsInPool);
Assert.assertEquals("Unexpected number of Unusable hosts found count.", 0, rule.strategy.twoUnusableHosts);
Assert.assertEquals("Unexpected number of no usable hosts count.", 1, rule.strategy.allUnusable);
}
@Test
public void testMultipleUnusableHost() {
ConnectionProvider<ByteBuf, ByteBuf> cp = rule.strategy.newStrategy(rule.newHostStream(UNUSABLE_HOST_WEIGHT,
UNUSABLE_HOST_WEIGHT));
TestSubscriber<Connection<ByteBuf, ByteBuf>> sub = new TestSubscriber<>();
cp.newConnectionRequest().subscribe(sub);
sub.awaitTerminalEvent();
sub.assertError(NoHostsAvailableException.class);
Assert.assertEquals("Unexpected number of hosts in the pool.", 2, rule.strategy.hostsInPool);
Assert.assertEquals("Unexpected number of Unusable hosts found count.", 5, rule.strategy.twoUnusableHosts);
Assert.assertEquals("Unexpected number of no usable hosts count.", 1, rule.strategy.allUnusable);
}
@Test
public void testUsableAndUnusable() {
ConnectionProvider<ByteBuf, ByteBuf> cp = rule.strategy.newStrategy(rule.newHostStream(10, UNUSABLE_HOST_WEIGHT));
TestSubscriber<Connection<ByteBuf, ByteBuf>> sub = new TestSubscriber<>();
cp.newConnectionRequest().subscribe(sub);
sub.awaitTerminalEvent();
sub.assertNoErrors();
Assert.assertEquals("Unexpected number of hosts in the pool.", 2, rule.strategy.hostsInPool);
Assert.assertEquals("Unexpected number of Unusable hosts found count.", 0, rule.strategy.twoUnusableHosts);
Assert.assertEquals("Unexpected number of no usable hosts count.", 0,rule.strategy.allUnusable);
}
public static class StrategyRule extends ExternalResource {
private MockP2CStrategy strategy;
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
strategy = new MockP2CStrategy();
base.evaluate();
}
};
}
public List<HostHolder<ByteBuf, ByteBuf>> newHostStream(int... weights) {
List<HostHolder<ByteBuf, ByteBuf>> toReturn = new ArrayList<>();
for (int weight : weights) {
ConnectionProvider<ByteBuf, ByteBuf> dummy = new ConnectionProvider<ByteBuf, ByteBuf>() {
@Override
public Observable<Connection<ByteBuf, ByteBuf>> newConnectionRequest() {
return Observable.empty();
}
};
Host h = new Host(new InetSocketAddress(0));
EventPublisher publisher = MockEventPublisher.disabled();
HostConnector<ByteBuf, ByteBuf> connector = new HostConnector<>(h, dummy, null, publisher, null);
toReturn.add(new HostHolder<>(connector, new ClientListenerImpl(weight)));
}
return toReturn;
}
private static class ClientListenerImpl extends ClientEventListener {
private volatile double weight;
public ClientListenerImpl(double weight) {
this.weight = weight;
}
public double getWeight() {
return weight;
}
}
private static class MockP2CStrategy extends AbstractP2CStrategy<ByteBuf, ByteBuf, ClientListenerImpl> {
private volatile int allUnusable;
private volatile int hostsInPool;
private volatile int twoUnusableHosts;
@Override
protected ClientListenerImpl newListener(Host host) {
return new ClientListenerImpl(0);
}
@Override
protected double getWeight(ClientListenerImpl listener) {
return listener.getWeight();
}
@Override
protected void noUsableHostsFound() {
allUnusable++;
}
@Override
protected void foundTwoUnusableHosts() {
twoUnusableHosts++;
}
@Override
protected void newHostsList(int size) {
hostsInPool += size;
}
}
}
}