This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch maven2surefire-jvm-communication in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit b1a4930c554c7b9a4aad36d8465a4571dad42f60 Author: tibordigana <tibordig...@apache.org> AuthorDate: Sun Apr 5 02:25:41 2020 +0200 improved coverage in new code --- .../org/apache/maven/surefire/JUnit4SuiteTest.java | 2 - .../maven/surefire/util/internal/Channels.java | 14 +- .../java/org/apache/maven/JUnit4SuiteTest.java | 4 +- .../surefire/util/internal}/AsyncSocketTest.java | 3 +- .../surefire/util/internal/ChannelsReaderTest.java | 239 ++++++++++++++++++++ .../surefire/util/internal/ChannelsWriterTest.java | 243 +++++++++++++++++++++ .../surefire/booter/ForkedBooterMockTest.java | 36 ++- .../maven/surefire/booter/ForkedBooterTest.java | 20 +- 8 files changed, 549 insertions(+), 12 deletions(-) diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java index 94754a7..22bf702 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java @@ -40,7 +40,6 @@ import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfigu import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStreamBuilderTest; import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStreamTest; import org.apache.maven.plugin.surefire.booterclient.output.ForkClientTest; -import org.apache.maven.plugin.surefire.extensions.AsyncSocketTest; import org.apache.maven.plugin.surefire.extensions.ConsoleOutputReporterTest; import org.apache.maven.plugin.surefire.extensions.E2ETest; import org.apache.maven.plugin.surefire.extensions.ForkedProcessEventNotifierTest; @@ -111,7 +110,6 @@ public class JUnit4SuiteTest extends TestCase suite.addTest( new JUnit4TestAdapter( ForkChannelTest.class ) ); suite.addTest( new JUnit4TestAdapter( StreamFeederTest.class ) ); suite.addTest( new JUnit4TestAdapter( E2ETest.class ) ); - suite.addTest( new JUnit4TestAdapter( AsyncSocketTest.class ) ); return suite; } } diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/Channels.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/Channels.java index a536fc6..b15e6f6 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/Channels.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/Channels.java @@ -28,7 +28,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousByteChannel; -import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.concurrent.ExecutionException; @@ -99,7 +99,9 @@ public final class Channels catch ( ExecutionException e ) { Throwable t = e.getCause(); - throw new IOException( ( t == null ? e : t ).getLocalizedMessage(), t ); + throw t instanceof IOException + ? (IOException) t + : new IOException( ( t == null ? e : t ).getLocalizedMessage(), t ); } catch ( Exception e ) { @@ -124,7 +126,7 @@ public final class Channels { channel.close(); } - catch ( AsynchronousCloseException e ) + catch ( ClosedChannelException e ) { // closed channel anyway } @@ -157,7 +159,9 @@ public final class Channels catch ( ExecutionException e ) { Throwable t = e.getCause(); - throw new IOException( ( t == null ? e : t ).getLocalizedMessage(), t ); + throw t instanceof IOException + ? (IOException) t + : new IOException( ( t == null ? e : t ).getLocalizedMessage(), t ); } catch ( Exception e ) { @@ -188,7 +192,7 @@ public final class Channels { channel.close(); } - catch ( AsynchronousCloseException e ) + catch ( ClosedChannelException e ) { // closed channel anyway } diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java index 650807f..231ff74 100644 --- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java +++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java @@ -35,6 +35,7 @@ import org.apache.maven.surefire.util.RunOrderCalculatorTest; import org.apache.maven.surefire.util.RunOrderTest; import org.apache.maven.surefire.util.ScanResultTest; import org.apache.maven.surefire.util.TestsToRunTest; +import org.apache.maven.surefire.util.internal.AsyncSocketTest; import org.apache.maven.surefire.util.internal.ChannelsReaderTest; import org.apache.maven.surefire.util.internal.ChannelsWriterTest; import org.apache.maven.surefire.util.internal.ConcurrencyUtilsTest; @@ -66,7 +67,8 @@ import org.junit.runners.Suite; ImmutableMapTest.class, ReflectionUtilsTest.class, ChannelsReaderTest.class, - ChannelsWriterTest.class + ChannelsWriterTest.class, + AsyncSocketTest.class } ) @RunWith( Suite.class ) public class JUnit4SuiteTest diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/AsyncSocketTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/AsyncSocketTest.java similarity index 98% rename from maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/AsyncSocketTest.java rename to surefire-api/src/test/java/org/apache/maven/surefire/util/internal/AsyncSocketTest.java index 88cba07..8363e29 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/AsyncSocketTest.java +++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/AsyncSocketTest.java @@ -1,4 +1,4 @@ -package org.apache.maven.plugin.surefire.extensions; +package org.apache.maven.surefire.util.internal; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -19,7 +19,6 @@ package org.apache.maven.plugin.surefire.extensions; * under the License. */ -import org.apache.maven.surefire.util.internal.DaemonThreadFactory; import org.junit.Test; import java.io.BufferedInputStream; diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsReaderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsReaderTest.java index 0b64a42..8a3b0a4 100644 --- a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsReaderTest.java +++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsReaderTest.java @@ -23,19 +23,37 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InterruptedIOException; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousByteChannel; import java.nio.channels.ClosedChannelException; import java.nio.channels.NonReadableChannelException; import java.nio.channels.ReadableByteChannel; +import java.nio.channels.ShutdownChannelGroupException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import static java.nio.file.Files.write; import static org.fest.assertions.Assertions.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; /** * The tests for {@link Channels#newChannel(InputStream)} and {@link Channels#newBufferedChannel(InputStream)}. @@ -303,4 +321,225 @@ public class ChannelsReaderTest assertThat( bb.array() ) .isEqualTo( new byte[] {1, 2, 3, 0} ); } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput1() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + InputStream is = Channels.newInputStream( channel ); + is.read( new byte[0], -1, 0 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput2() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + InputStream is = Channels.newInputStream( channel ); + is.read( new byte[0], 0, -1 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput3() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + InputStream is = Channels.newInputStream( channel ); + is.read( new byte[0], 1, 0 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput4() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + InputStream is = Channels.newInputStream( channel ); + is.read( new byte[0], 0, 1 ); + } + + @Test + public void shouldClose() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.isOpen() ).thenReturn( true ); + InputStream is = Channels.newInputStream( channel ); + is.close(); + verify( channel, times( 1 ) ).close(); + } + + @Test + public void shouldNotClose() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.isOpen() ).thenReturn( false ); + InputStream is = Channels.newInputStream( channel ); + is.close(); + verify( channel, never() ).close(); + } + + @Test + public void shouldAlreadyClosed() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.isOpen() ).thenReturn( true ); + doThrow( ClosedChannelException.class ).when( channel ).close(); + InputStream is = Channels.newInputStream( channel ); + is.close(); + verify( channel ).close(); + } + + @Test + public void shouldReadZeroLength() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + InputStream is = Channels.newInputStream( channel ); + is.read( new byte[] { 5 }, 0, 0 ); + verifyZeroInteractions( channel ); + } + + @Test + public void shouldReadArray() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.read( any( ByteBuffer.class ) ) ) + .thenAnswer( new Answer<Future<Integer>>() + { + @Override + public Future<Integer> answer( InvocationOnMock invocation ) throws Throwable + { + ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0]; + bb.put( (byte) 3 ); + bb.put( (byte) 4 ); + Future<Integer> future = mock( Future.class ); + when( future.get() ).thenReturn( 2 ); + return future; + } + } ); + + InputStream is = Channels.newInputStream( channel ); + ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass( ByteBuffer.class ); + byte[] b = new byte[] { 1, 2, 0, 0, 5 }; + is.read( b, 2, 2 ); + + verify( channel ).read( captured.capture() ); + verifyNoMoreInteractions( channel ); + + assertThat( captured.getAllValues() ) + .hasSize( 1 ); + + assertThat( captured.getAllValues().get( 0 ).array() ) + .containsOnly( new byte[] { 1, 2, 3, 4, 5 } ); + + assertThat( captured.getAllValues().get( 0 ).arrayOffset() ) + .isEqualTo( 0 ); + + assertThat( captured.getAllValues().get( 0 ).position() ) + .isEqualTo( 4 ); + + assertThat( captured.getAllValues().get( 0 ).limit() ) + .isEqualTo( 4 ); + + assertThat( captured.getAllValues().get( 0 ).capacity() ) + .isEqualTo( 5 ); + } + + @Test + public void shouldRead() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.read( any( ByteBuffer.class ) ) ) + .thenAnswer( new Answer<Future<Integer>>() + { + @Override + public Future<Integer> answer( InvocationOnMock invocation ) throws Throwable + { + ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0]; + bb.put( (byte) 3 ); + Future<Integer> future = mock( Future.class ); + when( future.get() ).thenReturn( 1 ); + return future; + } + } ); + + InputStream is = Channels.newInputStream( channel ); + ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass( ByteBuffer.class ); + int b = is.read(); + assertThat( b ) + .isEqualTo( 3 ); + + verify( channel ).read( captured.capture() ); + verifyNoMoreInteractions( channel ); + + assertThat( captured.getAllValues() ) + .hasSize( 1 ); + + assertThat( captured.getAllValues().get( 0 ).array() ) + .containsOnly( new byte[] { 3 } ); + + assertThat( captured.getAllValues().get( 0 ).arrayOffset() ) + .isEqualTo( 0 ); + + assertThat( captured.getAllValues().get( 0 ).position() ) + .isEqualTo( 1 ); + + assertThat( captured.getAllValues().get( 0 ).limit() ) + .isEqualTo( 1 ); + + assertThat( captured.getAllValues().get( 0 ).capacity() ) + .isEqualTo( 1 ); + } + + @Test + public void shouldThrowExceptionOnRead() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.read( any( ByteBuffer.class ) ) ) + .thenThrow( ShutdownChannelGroupException.class ); + InputStream is = Channels.newInputStream( channel ); + ee.expect( IOException.class ); + ee.expectCause( instanceOf( ShutdownChannelGroupException.class ) ); + is.read( new byte[1], 0, 1 ); + } + + @Test + public void shouldThrowExceptionOnFuture1() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + Future<Integer> future = mock( Future.class ); + when( future.get() ) + .thenThrow( new ExecutionException( new InterruptedIOException() ) ); + when( channel.read( any( ByteBuffer.class ) ) ) + .thenReturn( future ); + InputStream is = Channels.newInputStream( channel ); + ee.expect( InterruptedIOException.class ); + is.read( new byte[1], 0, 1 ); + } + + @Test + public void shouldThrowExceptionOnFuture2() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + Future<Integer> future = mock( Future.class ); + when( future.get() ) + .thenThrow( new ExecutionException( new RuntimeException( "msg" ) ) ); + when( channel.read( any( ByteBuffer.class ) ) ) + .thenReturn( future ); + InputStream is = Channels.newInputStream( channel ); + ee.expect( IOException.class ); + ee.expectCause( instanceOf( RuntimeException.class ) ); + ee.expectMessage( "msg" ); + is.read( new byte[1], 0, 1 ); + } + + @Test + public void shouldThrowExceptionOnFuture3() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + Future<Integer> future = mock( Future.class ); + when( future.get() ) + .thenThrow( new ExecutionException( "msg", null ) ); + when( channel.read( any( ByteBuffer.class ) ) ) + .thenReturn( future ); + InputStream is = Channels.newInputStream( channel ); + ee.expect( IOException.class ); + ee.expectMessage( "msg" ); + is.read( new byte[1], 0, 1 ); + } } diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsWriterTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsWriterTest.java index 35c9ddd..24ed3f3 100644 --- a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsWriterTest.java +++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ChannelsWriterTest.java @@ -23,19 +23,37 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousByteChannel; import java.nio.channels.ClosedChannelException; import java.nio.channels.NonWritableChannelException; +import java.nio.channels.ShutdownChannelGroupException; import java.nio.channels.WritableByteChannel; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import static java.nio.file.Files.readAllBytes; import static org.fest.assertions.Assertions.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; /** * The tests for {@link Channels#newChannel(OutputStream)} and {@link Channels#newBufferedChannel(OutputStream)}. @@ -207,4 +225,229 @@ public class ChannelsWriterTest .hasSize( 3 ) .isEqualTo( new byte[] {1, 2, 3} ); } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput1() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + OutputStream os = Channels.newOutputStream( channel ); + os.write( new byte[0], -1, 0 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput2() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + OutputStream os = Channels.newOutputStream( channel ); + os.write( new byte[0], 0, -1 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput3() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + OutputStream os = Channels.newOutputStream( channel ); + os.write( new byte[0], 1, 0 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void shouldValidateInput4() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + OutputStream os = Channels.newOutputStream( channel ); + os.write( new byte[0], 0, 1 ); + } + + @Test + public void shouldClose() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.isOpen() ).thenReturn( true ); + OutputStream os = Channels.newOutputStream( channel ); + os.close(); + verify( channel, times( 1 ) ).close(); + } + + @Test + public void shouldNotClose() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.isOpen() ).thenReturn( false ); + OutputStream os = Channels.newOutputStream( channel ); + os.close(); + verify( channel, never() ).close(); + } + + @Test + public void shouldAlreadyClosed() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.isOpen() ).thenReturn( true ); + doThrow( ClosedChannelException.class ).when( channel ).close(); + OutputStream os = Channels.newOutputStream( channel ); + os.close(); + verify( channel ).close(); + } + + @Test + public void shouldWriteZeroLength() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + OutputStream os = Channels.newOutputStream( channel ); + os.write( new byte[] { 5 }, 0, 0 ); + verifyZeroInteractions( channel ); + } + + @Test + public void shouldWriteArray() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.write( any( ByteBuffer.class ) ) ) + .thenAnswer( new Answer<Future<Integer>>() + { + @Override + public Future<Integer> answer( InvocationOnMock invocation ) throws Throwable + { + ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0]; + int i = 0; + for ( ; bb.hasRemaining(); i++ ) + { + bb.get(); + } + Future<Integer> future = mock( Future.class ); + when( future.get() ).thenReturn( i ); + return future; + } + } ); + + OutputStream os = Channels.newOutputStream( channel ); + ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass( ByteBuffer.class ); + os.write( new byte[] { 1, 2, 3, 4, 5 }, 2, 2 ); + + verify( channel ).write( captured.capture() ); + verifyNoMoreInteractions( channel ); + + assertThat( captured.getAllValues() ) + .hasSize( 1 ); + + assertThat( captured.getAllValues().get( 0 ).array() ) + .containsOnly( new byte[] { 1, 2, 3, 4, 5 } ); + + assertThat( captured.getAllValues().get( 0 ).arrayOffset() ) + .isEqualTo( 0 ); + + assertThat( captured.getAllValues().get( 0 ).position() ) + .isEqualTo( 4 ); + + assertThat( captured.getAllValues().get( 0 ).limit() ) + .isEqualTo( 4 ); + + assertThat( captured.getAllValues().get( 0 ).capacity() ) + .isEqualTo( 5 ); + } + + @Test + public void shouldWrite() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.write( any( ByteBuffer.class ) ) ) + .thenAnswer( new Answer<Future<Integer>>() + { + @Override + public Future<Integer> answer( InvocationOnMock invocation ) throws Throwable + { + ByteBuffer bb = (ByteBuffer) invocation.getArguments()[0]; + int i = 0; + for ( ; bb.hasRemaining(); i++ ) + { + bb.get(); + } + Future<Integer> future = mock( Future.class ); + when( future.get() ).thenReturn( i ); + return future; + } + } ); + + OutputStream os = Channels.newOutputStream( channel ); + ArgumentCaptor<ByteBuffer> captured = ArgumentCaptor.forClass( ByteBuffer.class ); + os.write( 3 ); + + verify( channel ).write( captured.capture() ); + verifyNoMoreInteractions( channel ); + + assertThat( captured.getAllValues() ) + .hasSize( 1 ); + + assertThat( captured.getAllValues().get( 0 ).array() ) + .containsOnly( new byte[] { 3 } ); + + assertThat( captured.getAllValues().get( 0 ).arrayOffset() ) + .isEqualTo( 0 ); + + assertThat( captured.getAllValues().get( 0 ).position() ) + .isEqualTo( 1 ); + + assertThat( captured.getAllValues().get( 0 ).limit() ) + .isEqualTo( 1 ); + + assertThat( captured.getAllValues().get( 0 ).capacity() ) + .isEqualTo( 1 ); + } + + @Test + public void shouldThrowExceptionOnWrite() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + when( channel.write( any( ByteBuffer.class ) ) ) + .thenThrow( ShutdownChannelGroupException.class ); + OutputStream os = Channels.newOutputStream( channel ); + ee.expect( IOException.class ); + ee.expectCause( instanceOf( ShutdownChannelGroupException.class ) ); + os.write( new byte[1], 0, 1 ); + } + + @Test + public void shouldThrowExceptionOnFuture1() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + Future<Integer> future = mock( Future.class ); + when( future.get() ) + .thenThrow( new ExecutionException( new InterruptedIOException() ) ); + when( channel.write( any( ByteBuffer.class ) ) ) + .thenReturn( future ); + OutputStream os = Channels.newOutputStream( channel ); + ee.expect( InterruptedIOException.class ); + os.write( new byte[1], 0, 1 ); + } + + @Test + public void shouldThrowExceptionOnFuture2() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + Future<Integer> future = mock( Future.class ); + when( future.get() ) + .thenThrow( new ExecutionException( new RuntimeException( "msg" ) ) ); + when( channel.write( any( ByteBuffer.class ) ) ) + .thenReturn( future ); + OutputStream os = Channels.newOutputStream( channel ); + ee.expect( IOException.class ); + ee.expectCause( instanceOf( RuntimeException.class ) ); + ee.expectMessage( "msg" ); + os.write( new byte[1], 0, 1 ); + } + + @Test + public void shouldThrowExceptionOnFuture3() throws Exception + { + AsynchronousByteChannel channel = mock( AsynchronousByteChannel.class ); + Future<Integer> future = mock( Future.class ); + when( future.get() ) + .thenThrow( new ExecutionException( "msg", null ) ); + when( channel.write( any( ByteBuffer.class ) ) ) + .thenReturn( future ); + OutputStream os = Channels.newOutputStream( channel ); + ee.expect( IOException.class ); + ee.expectMessage( "msg" ); + os.write( new byte[1], 0, 1 ); + } } diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java index 5604fd1..4a3690e 100644 --- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java +++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java @@ -24,6 +24,7 @@ import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder; import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory; import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory; import org.apache.maven.surefire.report.StackTraceWriter; +import org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils; import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory; import org.junit.Rule; import org.junit.Test; @@ -33,6 +34,8 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -50,9 +53,11 @@ import static org.fest.assertions.Fail.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.powermock.api.mockito.PowerMockito.doAnswer; import static org.powermock.api.mockito.PowerMockito.doCallRealMethod; import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.doThrow; @@ -67,7 +72,12 @@ import static org.powermock.reflect.Whitebox.setInternalState; * PowerMock tests for {@link ForkedBooter}. */ @RunWith( PowerMockRunner.class ) -@PrepareForTest( { PpidChecker.class, ForkedBooter.class, LegacyMasterProcessChannelEncoder.class } ) +@PrepareForTest( { + PpidChecker.class, + ForkedBooter.class, + LegacyMasterProcessChannelEncoder.class, + ShutdownHookUtils.class +} ) @PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } ) public class ForkedBooterMockTest { @@ -349,4 +359,28 @@ public class ForkedBooterMockTest } } } + + @Test + public void testFlushEventChannelOnExit() throws Exception + { + mockStatic( ShutdownHookUtils.class ); + + final MasterProcessChannelEncoder eventChannel = mock( MasterProcessChannelEncoder.class ); + ForkedBooter booter = new ForkedBooter(); + setInternalState( booter, "eventChannel", eventChannel ); + + doAnswer( new Answer<Object>() + { + @Override + public Object answer( InvocationOnMock invocation ) + { + Thread t = invocation.getArgument( 0 ); + assertThat( t.isDaemon() ).isTrue(); + t.run(); + verify( eventChannel, times( 1 ) ).onJvmExit(); + return null; + } + } ).when( ShutdownHookUtils.class, "addShutDownHook", any( Thread.class ) ); + invokeMethod( booter, "flushEventChannelOnExit" ); + } } diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterTest.java index aac78f7..ef2ba61 100644 --- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterTest.java +++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterTest.java @@ -148,7 +148,7 @@ public class ForkedBooterTest } @Test( timeout = 10_000 ) - public void testBarrier() throws Exception + public void testBarrier1() throws Exception { Semaphore semaphore = new Semaphore( 2 ); boolean acquiredOnePermit = invokeMethod( ForkedBooter.class, "acquireOnePermit", semaphore, 30_000L ); @@ -158,6 +158,24 @@ public class ForkedBooterTest } @Test + public void testBarrier2() throws Exception + { + Semaphore semaphore = new Semaphore( 0 ); + Thread.currentThread().interrupt(); + try + { + boolean acquiredOnePermit = invokeMethod( ForkedBooter.class, "acquireOnePermit", semaphore, 30_000L ); + + assertThat( acquiredOnePermit ).isTrue(); + assertThat( semaphore.availablePermits() ).isEqualTo( 0 ); + } + finally + { + assertThat( Thread.interrupted() ).isFalse(); + } + } + + @Test public void testScheduler() throws Exception { ScheduledThreadPoolExecutor executor = invokeMethod( ForkedBooter.class, "createPingScheduler" );