From 9ffdec302e21e6a027275b2a1ac9a17a223a024f Mon Sep 17 00:00:00 2001 From: Alexey Kachayev Date: Fri, 22 Jun 2018 19:50:22 +0300 Subject: [PATCH] Make results of handler proxy tests reproducible Motivation: `ProxyHandlerTest` relies on random values to run tests: first to shuffle collection of test items and lately to set configuration flag for `AUTO_READ`. While the purpose of randomization is clear, it's still impossible to reproduce the same sequence of test cases when something went wrong. For `AUTO_READ` it's even impossible to tell what flag was set when the particular test failed. Modifications: * Test runner now log seed values that was used for shuffling, so you can take one and put in your tests to "freeze" them while debugging (pretty common approach with randomized tests) * `SuccessItemTest` is split into 2 different use cases: for AUTO_READ flag set to "on" and "off" Result: You can reproduce specific tests results now. --- .../netty/handler/proxy/ProxyHandlerTest.java | 136 +++++++++++++++--- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java index e79bed7cf5..047b2af09b 100644 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java +++ b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java @@ -63,8 +63,8 @@ import java.util.List; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.Random; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -130,15 +130,27 @@ public class ProxyHandlerTest { deadSocks5Proxy, interSocks5Proxy, anonSocks5Proxy, socks5Proxy ); + // set to non-zero value in case you need predictable shuffling of test cases + // look for "Seed used: *" debug message in test logs + private static final long reproducibleSeed = 0L; + @Parameters(name = "{index}: {0}") public static List testItems() { + List items = Arrays.asList( // HTTP ------------------------------------------------------- new SuccessTestItem( - "Anonymous HTTP proxy: successful connection", + "Anonymous HTTP proxy: successful connection, AUTO_READ on", DESTINATION, + true, + new HttpProxyHandler(anonHttpProxy.address())), + + new SuccessTestItem( + "Anonymous HTTP proxy: successful connection, AUTO_READ off", + DESTINATION, + false, new HttpProxyHandler(anonHttpProxy.address())), new FailureTestItem( @@ -152,8 +164,15 @@ public class ProxyHandlerTest { new HttpProxyHandler(httpProxy.address())), new SuccessTestItem( - "HTTP proxy: successful connection", + "HTTP proxy: successful connection, AUTO_READ on", DESTINATION, + true, + new HttpProxyHandler(httpProxy.address(), USERNAME, PASSWORD)), + + new SuccessTestItem( + "HTTP proxy: successful connection, AUTO_READ off", + DESTINATION, + false, new HttpProxyHandler(httpProxy.address(), USERNAME, PASSWORD)), new FailureTestItem( @@ -173,8 +192,16 @@ public class ProxyHandlerTest { // HTTPS ------------------------------------------------------ new SuccessTestItem( - "Anonymous HTTPS proxy: successful connection", + "Anonymous HTTPS proxy: successful connection, AUTO_READ on", DESTINATION, + true, + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(anonHttpsProxy.address())), + + new SuccessTestItem( + "Anonymous HTTPS proxy: successful connection, AUTO_READ off", + DESTINATION, + false, clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), new HttpProxyHandler(anonHttpsProxy.address())), @@ -191,8 +218,16 @@ public class ProxyHandlerTest { new HttpProxyHandler(httpsProxy.address())), new SuccessTestItem( - "HTTPS proxy: successful connection", + "HTTPS proxy: successful connection, AUTO_READ on", DESTINATION, + true, + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(httpsProxy.address(), USERNAME, PASSWORD)), + + new SuccessTestItem( + "HTTPS proxy: successful connection, AUTO_READ off", + DESTINATION, + false, clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), new HttpProxyHandler(httpsProxy.address(), USERNAME, PASSWORD)), @@ -216,8 +251,15 @@ public class ProxyHandlerTest { // SOCKS4 ----------------------------------------------------- new SuccessTestItem( - "Anonymous SOCKS4: successful connection", + "Anonymous SOCKS4: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks4ProxyHandler(anonSocks4Proxy.address())), + + new SuccessTestItem( + "Anonymous SOCKS4: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks4ProxyHandler(anonSocks4Proxy.address())), new FailureTestItem( @@ -231,8 +273,15 @@ public class ProxyHandlerTest { new Socks4ProxyHandler(socks4Proxy.address())), new SuccessTestItem( - "SOCKS4: successful connection", + "SOCKS4: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks4ProxyHandler(socks4Proxy.address(), USERNAME)), + + new SuccessTestItem( + "SOCKS4: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks4ProxyHandler(socks4Proxy.address(), USERNAME)), new FailureTestItem( @@ -252,8 +301,15 @@ public class ProxyHandlerTest { // SOCKS5 ----------------------------------------------------- new SuccessTestItem( - "Anonymous SOCKS5: successful connection", + "Anonymous SOCKS5: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks5ProxyHandler(anonSocks5Proxy.address())), + + new SuccessTestItem( + "Anonymous SOCKS5: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks5ProxyHandler(anonSocks5Proxy.address())), new FailureTestItem( @@ -267,8 +323,15 @@ public class ProxyHandlerTest { new Socks5ProxyHandler(socks5Proxy.address())), new SuccessTestItem( - "SOCKS5: successful connection", + "SOCKS5: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks5ProxyHandler(socks5Proxy.address(), USERNAME, PASSWORD)), + + new SuccessTestItem( + "SOCKS5: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks5ProxyHandler(socks5Proxy.address(), USERNAME, PASSWORD)), new FailureTestItem( @@ -288,8 +351,20 @@ public class ProxyHandlerTest { // HTTP + HTTPS + SOCKS4 + SOCKS5 new SuccessTestItem( - "Single-chain: successful connection", + "Single-chain: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 + new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(interHttpsProxy.address()), // HTTPS + new HttpProxyHandler(interHttpProxy.address()), // HTTP + new HttpProxyHandler(anonHttpProxy.address())), + + new SuccessTestItem( + "Single-chain: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), @@ -300,8 +375,25 @@ public class ProxyHandlerTest { // (HTTP + HTTPS + SOCKS4 + SOCKS5) * 2 new SuccessTestItem( - "Double-chain: successful connection", + "Double-chain: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 + new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(interHttpsProxy.address()), // HTTPS + new HttpProxyHandler(interHttpProxy.address()), // HTTP + new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 + new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(interHttpsProxy.address()), // HTTPS + new HttpProxyHandler(interHttpProxy.address()), // HTTP + new HttpProxyHandler(anonHttpProxy.address())), + + new SuccessTestItem( + "Double-chain: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), @@ -313,7 +405,6 @@ public class ProxyHandlerTest { new HttpProxyHandler(interHttpsProxy.address()), // HTTPS new HttpProxyHandler(interHttpProxy.address()), // HTTP new HttpProxyHandler(anonHttpProxy.address())) - ); // Convert the test items to the list of constructor parameters. @@ -323,7 +414,9 @@ public class ProxyHandlerTest { } // Randomize the execution order to increase the possibility of exposing failure dependencies. - Collections.shuffle(params); + long seed = (reproducibleSeed == 0L) ? System.currentTimeMillis() : reproducibleSeed; + logger.debug("Seed used: {}\n", seed); + Collections.shuffle(params, new Random(seed)); return params; } @@ -337,9 +430,7 @@ public class ProxyHandlerTest { private final TestItem testItem; - public ProxyHandlerTest(TestItem testItem) { - this.testItem = testItem; - } + public ProxyHandlerTest(TestItem testItem) { this.testItem = testItem; } @Before public void clearServerExceptions() throws Exception { @@ -516,8 +607,16 @@ public class ProxyHandlerTest { private static final class SuccessTestItem extends TestItem { private final int expectedEventCount; + // Probably we need to be more flexible here and as for the configuration map, + // not a single key. But as far as it works for now, I'm leaving the impl. + // as is, in case we need to cover more cases (like, AUTO_CLOSE, TCP_NODELAY etc) + // feel free to replace this boolean with either config or method to setup bootstrap + private final boolean autoRead; - SuccessTestItem(String name, InetSocketAddress destination, ChannelHandler... clientHandlers) { + SuccessTestItem(String name, + InetSocketAddress destination, + boolean autoRead, + ChannelHandler... clientHandlers) { super(name, destination, clientHandlers); int expectedEventCount = 0; for (ChannelHandler h: clientHandlers) { @@ -527,6 +626,7 @@ public class ProxyHandlerTest { } this.expectedEventCount = expectedEventCount; + this.autoRead = autoRead; } @Override @@ -535,7 +635,7 @@ public class ProxyHandlerTest { Bootstrap b = new Bootstrap(); b.group(group); b.channel(NioSocketChannel.class); - b.option(ChannelOption.AUTO_READ, ThreadLocalRandom.current().nextBoolean()); + b.option(ChannelOption.AUTO_READ, this.autoRead); b.resolver(NoopAddressResolverGroup.INSTANCE); b.handler(new ChannelInitializer() { @Override