diff --git a/src/test/scala/tlschannel/async/AsyncQuickCloseTest.java b/src/test/scala/tlschannel/async/AsyncQuickCloseTest.java new file mode 100644 index 00000000..fa8392c8 --- /dev/null +++ b/src/test/scala/tlschannel/async/AsyncQuickCloseTest.java @@ -0,0 +1,84 @@ +package tlschannel.async; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import tlschannel.helpers.SocketPairFactory; +import tlschannel.helpers.SslContextFactory; + +@TestInstance(Lifecycle.PER_CLASS) +public class AsyncQuickCloseTest implements AsyncTestBase { + + private final SslContextFactory sslContextFactory = new SslContextFactory(); + private final SocketPairFactory factory = new SocketPairFactory(sslContextFactory.defaultContext()); + + /* + * Closing sockets registered in an asynchronous channel group is inherently racy, using repetitions to try to catch + * most races. + */ + private final int repetitions = 250; + + private final int bufferSize = 10000; + + // see https://github.com/marianobarrios/tls-channel/issues/34 + // immediate closings after registration + @Test + public void testImmediateClose() throws IOException { + AsynchronousTlsChannelGroup channelGroup = new AsynchronousTlsChannelGroup(); + for (int i = 1; i <= repetitions; i++) { + // create (and register) channels and close immediately + tlschannel.helpers.SocketGroups.AsyncSocketPair socketPair = factory.async(null, channelGroup, true, false); + socketPair.server.external.close(); + socketPair.client.external.close(); + + // try read + ByteBuffer readBuffer = ByteBuffer.allocate(bufferSize); + Future readFuture = socketPair.server.external.read(readBuffer); + Exception e1 = Assertions.assertThrows(ExecutionException.class, () -> readFuture.get()); + assertInstanceOf(ClosedChannelException.class, e1.getCause()); + + // try write + Future writeFuture = socketPair.client.external.write(ByteBuffer.wrap(new byte[] {1})); + Exception e2 = Assertions.assertThrows(ExecutionException.class, () -> writeFuture.get()); + assertInstanceOf(ClosedChannelException.class, e2.getCause()); + } + assertTrue(channelGroup.isAlive()); + channelGroup.shutdown(); + assertChannelGroupConsistency(channelGroup); + } + + // immediate closings after registration, even if we close the raw channel + @Test + public void testRawImmediateClosing() throws IOException { + AsynchronousTlsChannelGroup channelGroup = new AsynchronousTlsChannelGroup(); + for (int i = 1; i <= repetitions; i++) { + // create (and register) channels and close immediately + tlschannel.helpers.SocketGroups.AsyncSocketPair socketPair = factory.async(null, channelGroup, true, false); + socketPair.server.plain.close(); + socketPair.client.plain.close(); + + // try read + ByteBuffer readBuffer = ByteBuffer.allocate(bufferSize); + Future readFuture = socketPair.server.external.read(readBuffer); + Exception readEx = Assertions.assertThrows(ExecutionException.class, () -> readFuture.get()); + assertInstanceOf(ClosedChannelException.class, readEx.getCause()); + + // try write + Future writeFuture = socketPair.client.external.write(ByteBuffer.wrap(new byte[] {1})); + Exception writeEx = Assertions.assertThrows(ExecutionException.class, () -> writeFuture.get()); + assertInstanceOf(ClosedChannelException.class, writeEx.getCause()); + } + assertTrue(channelGroup.isAlive()); + channelGroup.shutdown(); + assertChannelGroupConsistency(channelGroup); + } +} diff --git a/src/test/scala/tlschannel/async/AsyncQuickCloseTest.scala b/src/test/scala/tlschannel/async/AsyncQuickCloseTest.scala deleted file mode 100644 index adcea7bf..00000000 --- a/src/test/scala/tlschannel/async/AsyncQuickCloseTest.scala +++ /dev/null @@ -1,82 +0,0 @@ -package tlschannel.async - -import org.junit.jupiter.api.Assertions.{assertInstanceOf, assertTrue} -import org.junit.jupiter.api.TestInstance.Lifecycle -import tlschannel.helpers.{SocketPairFactory, SslContextFactory} - -import java.nio.ByteBuffer -import java.nio.channels.ClosedChannelException -import scala.concurrent.ExecutionException -import org.junit.jupiter.api.{Assertions, Test, TestInstance} - -@TestInstance(Lifecycle.PER_CLASS) -class AsyncQuickCloseTest extends AsyncTestBase { - - val sslContextFactory = new SslContextFactory - val factory = new SocketPairFactory(sslContextFactory.defaultContext) - - /* - * Closing sockets registered in an asynchronous channel group is inherently racy, using repetitions to try to catch - * most races. - */ - val repetitions = 250 - - val bufferSize = 10000 - - // see https://github.com/marianobarrios/tls-channel/issues/34 - // immediate closings after registration - @Test - def testImmediateClose(): Unit = { - val channelGroup = new AsynchronousTlsChannelGroup() - for (_ <- 1 to repetitions) { - - // create (and register) channels and close immediately - val socketPair = factory.async(null, channelGroup, runTasks = true) - socketPair.server.external.close() - socketPair.client.external.close() - - // try read - val readBuffer = ByteBuffer.allocate(bufferSize) - val readFuture = socketPair.server.external.read(readBuffer) - val e1 = Assertions.assertThrows(classOf[ExecutionException], () => readFuture.get()) - assertInstanceOf(classOf[ClosedChannelException], e1.getCause) - - // try write - val writeFuture = socketPair.client.external.write(ByteBuffer.wrap(Array(1))) - val e2 = Assertions.assertThrows(classOf[ExecutionException], () => writeFuture.get()) - assertInstanceOf(classOf[ClosedChannelException], e2.getCause) - } - - assertTrue(channelGroup.isAlive) - channelGroup.shutdown() - assertChannelGroupConsistency(channelGroup) - } - - // immediate closings after registration, even if we close the raw channel - @Test - def testRawImmediateClosing(): Unit = { - val channelGroup = new AsynchronousTlsChannelGroup() - for (_ <- 1 to repetitions) { - - // create (and register) channels and close immediately - val socketPair = factory.async(null, channelGroup, runTasks = true) - socketPair.server.plain.close() - socketPair.client.plain.close() - - // try read - val readBuffer = ByteBuffer.allocate(bufferSize) - val readFuture = socketPair.server.external.read(readBuffer) - val readEx = Assertions.assertThrows(classOf[ExecutionException], () => readFuture.get()) - assertInstanceOf(classOf[ClosedChannelException], readEx.getCause) - - // try write - val writeFuture = socketPair.client.external.write(ByteBuffer.wrap(Array(1))) - val writeEx = Assertions.assertThrows(classOf[ExecutionException], () => writeFuture.get()) - assertInstanceOf(classOf[ClosedChannelException], writeEx.getCause) - } - assertTrue(channelGroup.isAlive) - channelGroup.shutdown() - assertChannelGroupConsistency(channelGroup) - } - -}