package ru.decipher.proxy;
import org.junit.Before;
import org.junit.Test;
import ru.decipher.mock.Mock3GoodHttpProxyNoDelayLoader;
import ru.decipher.mock.Mock3GoodHttpProxySlowLoader;
import ru.decipher.mock.MockIncrementalGoodHttpProxyNoDelayLoader;
import ru.decipher.proxy.impl.DefaultProxyManager;
import ru.decipher.proxy.impl.HttpProxyConfig;
import ru.decipher.proxy.impl.LocalhostNoProxyLoader;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Thread.sleep;
import static junit.framework.Assert.*;
/**
* User: Alexander Paderin (apocarteres@gmail.com)
* Date: 1/17/14
* Time: 8:39 PM
*/
public class DefaultProxyManagerTest {
private static final HttpProxyConfig PROXY1 = new HttpProxyConfig("test1.host", 31337);
private static final HttpProxyConfig PROXY2 = new HttpProxyConfig("test2.host", 31337);
private static final HttpProxyConfig PROXY3 = new HttpProxyConfig("test3.host", 31337);
private static final HttpProxyConfig PROXY4 = new HttpProxyConfig("test4.host", 31337);
private ProxyManager manager;
@Before
public void setUp() throws Exception {
manager = new DefaultProxyManager(new MockIncrementalGoodHttpProxyNoDelayLoader(), 10, 100, 10, 0);
}
@Test
public void testThatProxyTakeWell() throws Exception {
assertEquals(PROXY1, manager.take());
}
@Test
public void testThatManagerReturnsProxyInAscendingOrder() throws Exception {
List<HttpProxyConfig> actual = new ArrayList<HttpProxyConfig>() {{
add(manager.take());
add(manager.take());
add(manager.take());
}};
List<HttpProxyConfig> expected = new ArrayList<HttpProxyConfig>() {{
add(PROXY1);
add(PROXY2);
add(PROXY3);
}};
assertEquals(expected, actual);
}
@Test
public void testThatProxyRotatingWell() throws Exception {
Set<HttpProxyConfig> expected = new HashSet<HttpProxyConfig>() {{
add(PROXY1);
add(PROXY2);
add(PROXY3);
}};
Set<HttpProxyConfig> actual = new LinkedHashSet<>();
for (int i = 0; i < 3; i++) {
actual.add(manager.take());
}
assertEquals(expected, actual);
}
@Test
public void testThatProxyBroughtBackWell() throws Exception {
final HttpProxyConfig proxy = manager.take();
for (int i = 0; i < 2; i++) {
manager.take();
}
manager.compliment(proxy);
assertEquals(PROXY1, manager.take());
}
@Test(expected = IllegalStateException.class)
public void testThatCanNotBackUnknownProxy() throws Exception {
manager.compliment(new HttpProxyConfig("testX.host", 31337));
}
@Test
public void testThatUsageRatePerProxyWorksWell() throws Exception {
long time = System.currentTimeMillis();
final HttpProxyConfig proxy = manager.take();
for (int i = 0; i < 2; i++) {
manager.take();
}
manager.compliment(proxy);
manager.take();
time = System.currentTimeMillis() - time;
assertTrue("proxy usage rate fails", time >= 100);
}
@Test
public void testThatUsageRatePerProxyWorksWellInConcurrency() throws Exception {
final DefaultProxyManager manager =
new DefaultProxyManager(new Mock3GoodHttpProxyNoDelayLoader(), 0, 1000, 10, 0);
class Task implements Runnable {
@Override
public void run() {
try {
final HttpProxyConfig proxy = manager.take();
//do something
manager.compliment(proxy);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
ExecutorService service = Executors.newFixedThreadPool(30);
Collection<Callable<Object>> tasks = new ArrayList<>();
for (int i = 0; i < 30; i++) {
tasks.add(Executors.callable(new Task()));
}
long time = System.currentTimeMillis();
final List<Future<Object>> futures = service.invokeAll(tasks);
for (Future<Object> future : futures) {
future.get();
}
time = System.currentTimeMillis() - time;
service.shutdown();
// 3 proxies with usage rate 1 second, thus we have performance: 3 task per 1 second
// 30 total tasks / 3 task = 10 seconds - 1 second for first fast call
// and total time of execution should be at least 9 sec
assertTrue("proxy usage rate in concurrency fails", time >= 9000);
}
@Test
public void testThatNewProxyWillBeLoadedFromSourceWhenAllProxiesAreDropped() throws Exception {
int blameTimes = 0;
while (blameTimes < 30) {
final HttpProxyConfig proxy = manager.take();
manager.blame(proxy);
blameTimes++;
}
assertEquals(PROXY1, manager.take());
}
@Test
public void testThatBadProxyWillBeGoneWhenFailedRequestsLimitHit() throws Exception {
int blameTimes = 0;
while (blameTimes < 10) {
final HttpProxyConfig proxy = manager.take();
if (proxy.equals(PROXY1)) {
blameTimes++;
}
manager.blame(proxy);
}
Set<HttpProxyConfig> expected = new HashSet<HttpProxyConfig>() {{
add(PROXY2);
add(PROXY3);
add(PROXY1); // returns back from proxy loader
}};
Set<HttpProxyConfig> actual = new LinkedHashSet<>();
for (int i = 0; i < 3; i++) {
actual.add(manager.take());
}
assertEquals(expected, actual);
}
@Test
public void testThatBadProxyWillBeForgivenOnSuccessRequest() throws Exception {
int blameTimes = 0;
boolean back = false;
while (blameTimes < 10) {
final HttpProxyConfig proxy = manager.take();
if (proxy.equals(PROXY1)) {
if (blameTimes == 1 && !back) {
manager.compliment(proxy);
back = true;
}
blameTimes++;
}
manager.blame(proxy);
}
Set<HttpProxyConfig> expected = new HashSet<HttpProxyConfig>() {{
add(PROXY1);
add(PROXY2);
add(PROXY3);
}};
Set<HttpProxyConfig> actual = new LinkedHashSet<>();
for (int i = 0; i < 3; i++) {
actual.add(manager.take());
}
assertEquals(expected, actual);
}
@Test(expected = IllegalStateException.class)
public void testThatDroppedProxyCanNotBeBroughtBack() throws Exception {
int blameTimes = 0;
while (blameTimes < 10) {
final HttpProxyConfig proxy = manager.take();
if (proxy.equals(PROXY1)) {
blameTimes++;
}
manager.blame(proxy);
}
manager.compliment(PROXY1);
}
@Test(expected = IllegalStateException.class)
public void testThatDroppedProxyCanNotBeBlamed() throws Exception {
int blameTimes = 0;
while (blameTimes < 10) {
final HttpProxyConfig proxy = manager.take();
if (proxy.equals(PROXY1)) {
blameTimes++;
}
manager.blame(proxy);
}
manager.blame(PROXY1);
}
@Test(expected = IllegalStateException.class)
public void testThatDroppedProxyCanNotBeBanned() throws Exception {
int blameTimes = 0;
while (blameTimes < 10) {
final HttpProxyConfig proxy = manager.take();
if (proxy.equals(PROXY1)) {
blameTimes++;
}
manager.blame(proxy);
}
manager.ban(PROXY1);
}
@Test
public void testConcurrentTakeAndBack() throws Exception {
final DefaultProxyManager manager = new DefaultProxyManager(new MockIncrementalGoodHttpProxyNoDelayLoader(), 10, 100, 10, 0);
ExecutorService service = Executors.newFixedThreadPool(20);
Collection<Callable<Object>> tasks = new ArrayList<>();
final AtomicInteger done = new AtomicInteger(0);
final Random random = new Random();
for (int i = 0; i < 100; i++) {
tasks.add(Executors.callable(new Runnable() {
@Override
public void run() {
HttpProxyConfig take = manager.take();
try {
sleep(100);
} catch (InterruptedException ignored) {
}
manager.compliment(take);
take = manager.take();
try {
sleep(100);
} catch (InterruptedException ignored) {
}
if (random.nextBoolean()) {
manager.blame(take);
} else {
manager.ban(take);
}
done.incrementAndGet();
}
}));
}
final List<Future<Object>> futures = service.invokeAll(tasks);
for (Future<Object> future : futures) {
future.get();
}
service.shutdown();
assertEquals(100, done.get());
}
@Test
public void testThatManagerDoesNotBlockQueueDuringProxiesUpdate() throws Exception {
manager = new DefaultProxyManager(new Mock3GoodHttpProxySlowLoader(), 1, 100, 10, 0);
final HttpProxyConfig firstProxy = manager.take();
final HttpProxyConfig secondProxy = manager.take();
manager.ban(secondProxy);
new Thread(new Runnable() {
@Override
public void run() {
manager.take();
}
}).start();
manager.compliment(firstProxy);
final HttpProxyConfig actual = manager.take();
assertNotNull(actual);
}
@Test
public void testThatCheckedProxiesGoFirst() throws Exception {
manager = new DefaultProxyManager(new MockIncrementalGoodHttpProxyNoDelayLoader(), 10, 100, 10, 0);
final HttpProxyConfig firstProxy = manager.take();
final HttpProxyConfig secondProxy = manager.take();
final HttpProxyConfig third = manager.take();
final HttpProxyConfig fourth = manager.take();
manager.blame(firstProxy);
manager.blame(secondProxy);
manager.compliment(fourth);
manager.compliment(third);
Set<HttpProxyConfig> expected = new HashSet<HttpProxyConfig>() {{
add(PROXY4);
add(PROXY3);
}};
Set<HttpProxyConfig> actual = new HashSet<HttpProxyConfig>() {{
add(manager.take());
add(manager.take());
}};
assertEquals(expected, actual);
}
@Test
public void testThatUpdateRateLimitWorksWell() throws Exception {
final DefaultProxyManager manager = new DefaultProxyManager(new LocalhostNoProxyLoader(), 0, 0, 0, 1000);
final HttpProxyConfig config = manager.take();
manager.ban(config);
long time = System.currentTimeMillis();
manager.take();
time = System.currentTimeMillis() - time;
assertTrue("update rate limits failed", time >= 1000);
}
}