DefaultPromise may throw checked exceptions that are not advertised (#8995)

Motivation:

We should not throw check exceptions when the user calls sync*() but should better wrap it in a CompletionException to make it easier for people to reason about what happens.

Modifications:

- Change sync*() to throw CompletionException
- Adjust tests
- Add some more tests

Result:

Fixes https://github.com/netty/netty/issues/8521.
This commit is contained in:
Norman Maurer 2019-04-10 07:15:31 +02:00 committed by GitHub
parent dbd2282abe
commit 7c35781f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 152 additions and 52 deletions

View File

@ -40,6 +40,7 @@ import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -443,7 +444,7 @@ public class Http2MultiplexCodecTest {
} }
@Test(expected = ClosedChannelException.class) @Test(expected = ClosedChannelException.class)
public void streamClosedErrorTranslatedToClosedChannelExceptionOnWrites() throws Exception { public void streamClosedErrorTranslatedToClosedChannelExceptionOnWrites() throws Throwable {
LastInboundHandler inboundHandler = new LastInboundHandler(); LastInboundHandler inboundHandler = new LastInboundHandler();
final Http2StreamChannel childChannel = newOutboundStream(inboundHandler); final Http2StreamChannel childChannel = newOutboundStream(inboundHandler);
@ -464,7 +465,11 @@ public class Http2MultiplexCodecTest {
inboundHandler.checkException(); inboundHandler.checkException();
try {
future.syncUninterruptibly(); future.syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
}
} }
@Test @Test
@ -502,7 +507,7 @@ public class Http2MultiplexCodecTest {
// likely happen due to the max concurrent streams limit being hit or the channel running out of stream identifiers. // likely happen due to the max concurrent streams limit being hit or the channel running out of stream identifiers.
// //
@Test(expected = Http2NoMoreStreamIdsException.class) @Test(expected = Http2NoMoreStreamIdsException.class)
public void failedOutboundStreamCreationThrowsAndClosesChannel() throws Exception { public void failedOutboundStreamCreationThrowsAndClosesChannel() throws Throwable {
LastInboundHandler handler = new LastInboundHandler(); LastInboundHandler handler = new LastInboundHandler();
Http2StreamChannel childChannel = newOutboundStream(handler); Http2StreamChannel childChannel = newOutboundStream(handler);
assertTrue(childChannel.isActive()); assertTrue(childChannel.isActive());
@ -522,7 +527,11 @@ public class Http2MultiplexCodecTest {
handler.checkException(); handler.checkException();
try {
future.syncUninterruptibly(); future.syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
}
} }
@Test @Test

View File

@ -24,6 +24,7 @@ import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@ -546,8 +547,10 @@ public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {
if (cause == null) { if (cause == null) {
return; return;
} }
if (cause instanceof CancellationException) {
PlatformDependent.throwException(cause); throw (CancellationException) cause;
}
throw new CompletionException(cause);
} }
private boolean await0(long timeoutNanos, boolean interruptable) throws InterruptedException { private boolean await0(long timeoutNanos, boolean interruptable) throws InterruptedException {

View File

@ -83,12 +83,20 @@ public interface Future<V> extends java.util.concurrent.Future<V> {
/** /**
* Waits for this future until it is done, and rethrows the cause of the failure if this future * Waits for this future until it is done, and rethrows the cause of the failure if this future
* failed. * failed.
*
* @throws CancellationException if the computation was cancelled
* @throws {@link java.util.concurrent.CompletionException} if the computation threw an exception.
* @throws InterruptedException if the current thread was interrupted while waiting
*
*/ */
Future<V> sync() throws InterruptedException; Future<V> sync() throws InterruptedException;
/** /**
* Waits for this future until it is done, and rethrows the cause of the failure if this future * Waits for this future until it is done, and rethrows the cause of the failure if this future
* failed. * failed.
*
* @throws CancellationException if the computation was cancelled
* @throws {@link java.util.concurrent.CompletionException} if the computation threw an exception.
*/ */
Future<V> syncUninterruptibly(); Future<V> syncUninterruptibly();

View File

@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -297,6 +298,39 @@ public class DefaultPromiseTest {
assertEquals("success", promise.getNow()); assertEquals("success", promise.getNow());
} }
@Test
public void throwUncheckedSync() throws InterruptedException {
Exception exception = new Exception();
final Promise<String> promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
promise.setFailure(exception);
try {
promise.sync();
} catch (CompletionException e) {
assertSame(exception, e.getCause());
}
}
@Test
public void throwUncheckedSyncUninterruptibly() {
Exception exception = new Exception();
final Promise<String> promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
promise.setFailure(exception);
try {
promise.syncUninterruptibly();
} catch (CompletionException e) {
assertSame(exception, e.getCause());
}
}
@Test(expected = CancellationException.class)
public void throwCancelled() throws InterruptedException {
final Promise<String> promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
promise.cancel(true);
promise.sync();
}
private static void testStackOverFlowChainedFuturesA(int promiseChainLength, final EventExecutor executor, private static void testStackOverFlowChainedFuturesA(int promiseChainLength, final EventExecutor executor,
boolean runTestInExecutorThread) boolean runTestInExecutorThread)
throws InterruptedException { throws InterruptedException {

View File

@ -15,12 +15,14 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import org.hamcrest.CoreMatchers;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -52,8 +54,9 @@ public class SingleThreadEventExecutorTest {
try { try {
executor.shutdownGracefully().syncUninterruptibly(); executor.shutdownGracefully().syncUninterruptibly();
Assert.fail(); Assert.fail();
} catch (RejectedExecutionException expected) { } catch (CompletionException expected) {
// expected // expected
Assert.assertThat(expected.getCause(), CoreMatchers.instanceOf(RejectedExecutionException.class));
} }
Assert.assertTrue(executor.isShutdown()); Assert.assertTrue(executor.isShutdown());
} }
@ -97,23 +100,39 @@ public class SingleThreadEventExecutorTest {
} }
@Test(expected = RejectedExecutionException.class, timeout = 3000) @Test(expected = RejectedExecutionException.class, timeout = 3000)
public void testInvokeAnyInEventLoop() { public void testInvokeAnyInEventLoop() throws Throwable {
try {
testInvokeInEventLoop(true, false); testInvokeInEventLoop(true, false);
} catch (CompletionException e) {
throw e.getCause();
}
} }
@Test(expected = RejectedExecutionException.class, timeout = 3000) @Test(expected = RejectedExecutionException.class, timeout = 3000)
public void testInvokeAnyInEventLoopWithTimeout() { public void testInvokeAnyInEventLoopWithTimeout() throws Throwable {
try {
testInvokeInEventLoop(true, true); testInvokeInEventLoop(true, true);
} catch (CompletionException e) {
throw e.getCause();
}
} }
@Test(expected = RejectedExecutionException.class, timeout = 3000) @Test(expected = RejectedExecutionException.class, timeout = 3000)
public void testInvokeAllInEventLoop() { public void testInvokeAllInEventLoop() throws Throwable {
try {
testInvokeInEventLoop(false, false); testInvokeInEventLoop(false, false);
} catch (CompletionException e) {
throw e.getCause();
}
} }
@Test(expected = RejectedExecutionException.class, timeout = 3000) @Test(expected = RejectedExecutionException.class, timeout = 3000)
public void testInvokeAllInEventLoopWithTimeout() { public void testInvokeAllInEventLoopWithTimeout() throws Throwable {
try {
testInvokeInEventLoop(false, true); testInvokeInEventLoop(false, true);
} catch (CompletionException e) {
throw e.getCause();
}
} }
private static void testInvokeInEventLoop(final boolean any, final boolean timeout) { private static void testInvokeInEventLoop(final boolean any, final boolean timeout) {

View File

@ -67,6 +67,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletionException;
/** /**
* In extra class to be able to run tests with java7 without trying to load classes that not exists in java7. * In extra class to be able to run tests with java7 without trying to load classes that not exists in java7.
@ -76,7 +77,7 @@ final class SniClientJava8TestUtil {
private SniClientJava8TestUtil() { } private SniClientJava8TestUtil() { }
static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider, final boolean match) static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider, final boolean match)
throws Exception { throws Throwable {
final String sniHost = "sni.netty.io"; final String sniHost = "sni.netty.io";
SelfSignedCertificate cert = new SelfSignedCertificate(); SelfSignedCertificate cert = new SelfSignedCertificate();
LocalAddress address = new LocalAddress("test"); LocalAddress address = new LocalAddress("test");
@ -150,6 +151,8 @@ final class SniClientJava8TestUtil {
promise.syncUninterruptibly(); promise.syncUninterruptibly();
sslHandler.handshakeFuture().syncUninterruptibly(); sslHandler.handshakeFuture().syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
} finally { } finally {
if (cc != null) { if (cc != null) {
cc.close().syncUninterruptibly(); cc.close().syncUninterruptibly();

View File

@ -27,7 +27,6 @@ import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalHandler; import io.netty.channel.local.LocalHandler;
import io.netty.channel.local.LocalServerChannel; import io.netty.channel.local.LocalServerChannel;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.Mapping;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
@ -80,12 +79,12 @@ public class SniClientTest {
} }
@Test(timeout = 30000) @Test(timeout = 30000)
public void testSniSNIMatcherMatchesClient() throws Exception { public void testSniSNIMatcherMatchesClient() throws Throwable {
SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, true); SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, true);
} }
@Test(timeout = 30000, expected = SSLException.class) @Test(timeout = 30000, expected = SSLException.class)
public void testSniSNIMatcherDoesNotMatchClient() throws Exception { public void testSniSNIMatcherDoesNotMatchClient() throws Throwable {
SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, false); SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, false);
} }

View File

@ -61,6 +61,7 @@ import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -127,12 +128,12 @@ public class SslHandlerTest {
} }
@Test(expected = SSLException.class, timeout = 3000) @Test(expected = SSLException.class, timeout = 3000)
public void testClientHandshakeTimeout() throws Exception { public void testClientHandshakeTimeout() throws Throwable {
testHandshakeTimeout(true); testHandshakeTimeout(true);
} }
@Test(expected = SSLException.class, timeout = 3000) @Test(expected = SSLException.class, timeout = 3000)
public void testServerHandshakeTimeout() throws Exception { public void testServerHandshakeTimeout() throws Throwable {
testHandshakeTimeout(false); testHandshakeTimeout(false);
} }
@ -146,7 +147,7 @@ public class SslHandlerTest {
return engine; return engine;
} }
private static void testHandshakeTimeout(boolean client) throws Exception { private static void testHandshakeTimeout(boolean client) throws Throwable {
SSLEngine engine = SSLContext.getDefault().createSSLEngine(); SSLEngine engine = SSLContext.getDefault().createSSLEngine();
engine.setUseClientMode(client); engine.setUseClientMode(client);
SslHandler handler = new SslHandler(engine); SslHandler handler = new SslHandler(engine);
@ -161,6 +162,8 @@ public class SslHandlerTest {
} }
handler.handshakeFuture().syncUninterruptibly(); handler.handshakeFuture().syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
} finally { } finally {
ch.finishAndReleaseAll(); ch.finishAndReleaseAll();
} }

View File

@ -83,6 +83,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@ -679,10 +680,11 @@ public class DnsNameResolverTest {
private static UnknownHostException resolveNonExistentDomain(DnsNameResolver resolver) { private static UnknownHostException resolveNonExistentDomain(DnsNameResolver resolver) {
try { try {
resolver.resolve("non-existent.netty.io").sync(); resolver.resolve("non-existent.netty.io").syncUninterruptibly();
fail(); fail();
return null; return null;
} catch (Exception e) { } catch (CompletionException cause) {
Throwable e = cause.getCause();
assertThat(e, is(instanceOf(UnknownHostException.class))); assertThat(e, is(instanceOf(UnknownHostException.class)));
TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory =
@ -2108,7 +2110,7 @@ public class DnsNameResolverTest {
} }
@Test @Test
public void testFollowCNAMELoop() throws IOException { public void testFollowCNAMELoop() throws Throwable {
expectedException.expect(UnknownHostException.class); expectedException.expect(UnknownHostException.class);
TestDnsServer dnsServer2 = new TestDnsServer(question -> { TestDnsServer dnsServer2 = new TestDnsServer(question -> {
Set<ResourceRecord> records = new LinkedHashSet<>(4); Set<ResourceRecord> records = new LinkedHashSet<>(4);
@ -2141,6 +2143,8 @@ public class DnsNameResolverTest {
resolver = builder.build(); resolver = builder.build();
resolver.resolveAll("somehost.netty.io").syncUninterruptibly().getNow(); resolver.resolveAll("somehost.netty.io").syncUninterruptibly().getNow();
} catch (CompletionException e) {
throw e.getCause();
} finally { } finally {
dnsServer2.stop(); dnsServer2.stop();
if (resolver != null) { if (resolver != null) {
@ -2150,24 +2154,26 @@ public class DnsNameResolverTest {
} }
@Test @Test
public void testSearchDomainQueryFailureForSingleAddressTypeCompletes() { public void testSearchDomainQueryFailureForSingleAddressTypeCompletes() throws Throwable {
expectedException.expect(UnknownHostException.class); expectedException.expect(UnknownHostException.class);
testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_ONLY); testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_ONLY);
} }
@Test @Test
public void testSearchDomainQueryFailureForMultipleAddressTypeCompletes() { public void testSearchDomainQueryFailureForMultipleAddressTypeCompletes() throws Throwable {
expectedException.expect(UnknownHostException.class); expectedException.expect(UnknownHostException.class);
testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_PREFERRED); testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_PREFERRED);
} }
private void testSearchDomainQueryFailureCompletes(ResolvedAddressTypes types) { private void testSearchDomainQueryFailureCompletes(ResolvedAddressTypes types) throws Throwable {
DnsNameResolver resolver = newResolver() DnsNameResolver resolver = newResolver()
.resolvedAddressTypes(types) .resolvedAddressTypes(types)
.ndots(1) .ndots(1)
.searchDomains(singletonList(".")).build(); .searchDomains(singletonList(".")).build();
try { try {
resolver.resolve("invalid.com").syncUninterruptibly(); resolver.resolve("invalid.com").syncUninterruptibly();
} catch (CompletionException cause) {
throw cause.getCause();
} finally { } finally {
resolver.close(); resolver.close();
} }

View File

@ -40,14 +40,14 @@ public class SocketChannelNotYetConnectedTest extends AbstractClientSocketTest {
ch.shutdownInput().syncUninterruptibly(); ch.shutdownInput().syncUninterruptibly();
fail(); fail();
} catch (Throwable cause) { } catch (Throwable cause) {
checkThrowable(cause); checkThrowable(cause.getCause());
} }
try { try {
ch.shutdownOutput().syncUninterruptibly(); ch.shutdownOutput().syncUninterruptibly();
fail(); fail();
} catch (Throwable cause) { } catch (Throwable cause) {
checkThrowable(cause); checkThrowable(cause.getCause());
} }
} finally { } finally {
ch.close().syncUninterruptibly(); ch.close().syncUninterruptibly();

View File

@ -113,13 +113,13 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
ch.shutdownInput().syncUninterruptibly(); ch.shutdownInput().syncUninterruptibly();
fail(); fail();
} catch (Throwable cause) { } catch (Throwable cause) {
checkThrowable(cause); checkThrowable(cause.getCause());
} }
try { try {
ch.shutdownOutput().syncUninterruptibly(); ch.shutdownOutput().syncUninterruptibly();
fail(); fail();
} catch (Throwable cause) { } catch (Throwable cause) {
checkThrowable(cause); checkThrowable(cause.getCause());
} }
} finally { } finally {
if (s != null) { if (s != null) {
@ -177,7 +177,7 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
ch.writeAndFlush(Unpooled.wrappedBuffer(new byte[]{ 2 })).sync(); ch.writeAndFlush(Unpooled.wrappedBuffer(new byte[]{ 2 })).sync();
fail(); fail();
} catch (Throwable cause) { } catch (Throwable cause) {
checkThrowable(cause); checkThrowable(cause.getCause());
} }
assertNull(h.writabilityQueue.poll()); assertNull(h.writabilityQueue.poll());
} finally { } finally {

View File

@ -84,7 +84,7 @@ public class EpollReuseAddrTest {
bootstrap.bind(future.channel().localAddress()).syncUninterruptibly(); bootstrap.bind(future.channel().localAddress()).syncUninterruptibly();
Assert.fail(); Assert.fail();
} catch (Exception e) { } catch (Exception e) {
Assert.assertTrue(e instanceof IOException); Assert.assertTrue(e.getCause() instanceof IOException);
} }
future.channel().close().syncUninterruptibly(); future.channel().close().syncUninterruptibly();
} }

View File

@ -26,6 +26,7 @@ import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.CompletionException;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import org.junit.After; import org.junit.After;
@ -87,18 +88,23 @@ public class EpollSocketTcpMd5Test {
} }
@Test(expected = ConnectTimeoutException.class) @Test(expected = ConnectTimeoutException.class)
public void testKeyMismatch() throws Exception { public void testKeyMismatch() throws Throwable {
server.config().setOption(EpollChannelOption.TCP_MD5SIG, server.config().setOption(EpollChannelOption.TCP_MD5SIG,
Collections.singletonMap(NetUtil.LOCALHOST4, SERVER_KEY)); Collections.singletonMap(NetUtil.LOCALHOST4, SERVER_KEY));
try {
EpollSocketChannel client = (EpollSocketChannel) new Bootstrap().group(GROUP) EpollSocketChannel client = (EpollSocketChannel) new Bootstrap().group(GROUP)
.channel(EpollSocketChannel.class) .channel(EpollSocketChannel.class)
.handler(new ChannelHandler() { }) .handler(new ChannelHandler() {
})
.option(EpollChannelOption.TCP_MD5SIG, .option(EpollChannelOption.TCP_MD5SIG,
Collections.singletonMap(NetUtil.LOCALHOST4, BAD_KEY)) Collections.singletonMap(NetUtil.LOCALHOST4, BAD_KEY))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
.connect(server.localAddress()).syncUninterruptibly().channel(); .connect(server.localAddress()).syncUninterruptibly().channel();
client.close().syncUninterruptibly(); client.close().syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
}
} }
@Test @Test

View File

@ -45,6 +45,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@ -203,7 +204,7 @@ public class BootstrapTest {
} }
@Test(expected = ConnectException.class, timeout = 10000) @Test(expected = ConnectException.class, timeout = 10000)
public void testLateRegistrationConnect() throws Exception { public void testLateRegistrationConnect() throws Throwable {
EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory());
LateRegisterHandler registerHandler = new LateRegisterHandler(); LateRegisterHandler registerHandler = new LateRegisterHandler();
try { try {
@ -215,6 +216,8 @@ public class BootstrapTest {
assertFalse(future.isDone()); assertFalse(future.isDone());
registerHandler.registerPromise().setSuccess(); registerHandler.registerPromise().setSuccess();
future.syncUninterruptibly(); future.syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
} finally { } finally {
group.shutdownGracefully(); group.shutdownGracefully();
} }

View File

@ -20,6 +20,7 @@ import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.LoggingHandler.Event; import io.netty.channel.LoggingHandler.Event;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
import org.hamcrest.Matchers;
import org.junit.Test; import org.junit.Test;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
@ -270,7 +271,7 @@ public class ReentrantChannelTest extends BaseChannelTest {
fail(); fail();
} catch (Throwable cce) { } catch (Throwable cce) {
// FIXME: shouldn't this contain the "intentional failure" exception? // FIXME: shouldn't this contain the "intentional failure" exception?
assertEquals(ClosedChannelException.class, cce.getClass()); assertThat(cce.getCause(), Matchers.instanceOf(ClosedChannelException.class));
} }
clientChannel.closeFuture().sync(); clientChannel.closeFuture().sync();

View File

@ -46,6 +46,7 @@ import org.junit.Test;
import java.net.ConnectException; import java.net.ConnectException;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -171,7 +172,8 @@ public class LocalChannelTest {
try { try {
cc.writeAndFlush(new Object()).sync(); cc.writeAndFlush(new Object()).sync();
fail("must raise a ClosedChannelException"); fail("must raise a ClosedChannelException");
} catch (Exception e) { } catch (CompletionException cause) {
Throwable e = cause.getCause();
assertThat(e, is(instanceOf(ClosedChannelException.class))); assertThat(e, is(instanceOf(ClosedChannelException.class)));
// Ensure that the actual write attempt on a closed channel was never made by asserting that // Ensure that the actual write attempt on a closed channel was never made by asserting that
// the ClosedChannelException has been created by AbstractUnsafe rather than transport implementations. // the ClosedChannelException has been created by AbstractUnsafe rather than transport implementations.
@ -814,12 +816,16 @@ public class LocalChannelTest {
} }
@Test(expected = ConnectException.class) @Test(expected = ConnectException.class)
public void testConnectionRefused() { public void testConnectionRefused() throws Throwable {
try {
Bootstrap sb = new Bootstrap(); Bootstrap sb = new Bootstrap();
sb.group(group1) sb.group(group1)
.channel(LocalChannel.class) .channel(LocalChannel.class)
.handler(new TestHandler()) .handler(new TestHandler())
.connect(LocalAddress.ANY).syncUninterruptibly(); .connect(LocalAddress.ANY).syncUninterruptibly();
} catch (CompletionException e) {
throw e.getCause();
}
} }
private static final class LatchChannelFutureListener extends CountDownLatch implements ChannelFutureListener { private static final class LatchChannelFutureListener extends CountDownLatch implements ChannelFutureListener {