While creating unit tests to handle some command line output and exceptions thrown in an application, I decided to use SystemErrRule and SystemOutRule to assist with this.
As this particular test is for the main entry point of the application, it is parsing the command line and calling System.exit(1) when invalid command line arguments have been provided.
However, including both of these causes Gradle to throw an exception when trying to run the task gradlew test
:
:test
Unexpected exception thrown.
org.gradle.messaging.remote.internal.MessageIOException: Could not read message from '/127.0.0.1:50239'.
at org.gradle.messaging.remote.internal.inet.SocketConnection.receive(SocketConnection.java:79)
at org.gradle.messaging.remote.internal.hub.MessageHub$ConnectionReceive.run(MessageHub.java:235)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: com.esotericsoftware.kryo.KryoException: java.io.IOException: An existing connection was forcibly closed by the remote host
at com.esotericsoftware.kryo.io.Input.fill(Input.java:141)
at com.esotericsoftware.kryo.io.Input.require(Input.java:159)
at com.esotericsoftware.kryo.io.Input.readByte(Input.java:255)
at org.gradle.internal.serialize.kryo.KryoBackedDecoder.readByte(KryoBackedDecoder.java:80)
at org.gradle.messaging.remote.internal.hub.InterHubMessageSerializer$MessageReader.read(InterHubMessageSerializer.java:69)
at org.gradle.messaging.remote.internal.hub.InterHubMessageSerializer$MessageReader.read(InterHubMessageSerializer.java:58)
at org.gradle.messaging.remote.internal.inet.SocketConnection.receive(SocketConnection.java:74)
... 6 more
Caused by: java.io.IOException: An existing connection was forcibly closed by the remote host
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(Unknown Source)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
at sun.nio.ch.IOUtil.read(Unknown Source)
at sun.nio.ch.SocketChannelImpl.read(Unknown Source)
at org.gradle.messaging.remote.internal.inet.SocketConnection$SocketInputStream.read(SocketConnection.java:158)
at com.esotericsoftware.kryo.io.Input.fill(Input.java:139)
... 12 more
Unexpected exception thrown.
org.gradle.messaging.remote.internal.MessageIOException: Could not write message [EndOfStream] to '/127.0.0.1:50239'.
at org.gradle.messaging.remote.internal.inet.SocketConnection.dispatch(SocketConnection.java:106)
at org.gradle.messaging.remote.internal.hub.MessageHub$ConnectionDispatch.run(MessageHub.java:284)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.io.IOException: An existing connection was forcibly closed by the remote host
at sun.nio.ch.SocketDispatcher.write0(Native Method)
at sun.nio.ch.SocketDispatcher.write(Unknown Source)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(Unknown Source)
at sun.nio.ch.IOUtil.write(Unknown Source)
at sun.nio.ch.SocketChannelImpl.write(Unknown Source)
at org.gradle.messaging.remote.internal.inet.SocketConnection$SocketOutputStream.flush(SocketConnection.java:221)
at org.gradle.messaging.remote.internal.inet.SocketConnection.dispatch(SocketConnection.java:104)
... 6 more
:test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> Process 'Gradle Test Executor 1' finished with non-zero exit value 1
In IntelliJ, trying to run this test results in the error response "Failed to start: 0 passed, 1 not started" - so it isn't isolated to Gradle.
Here is an example setup that can reproduce this issue:
Program.java:
package org.test.example;
public final class Program
{
public static void main(String[] args)
{
if (args.length != 2)
{
System.out.println("Proper usage: -port <number>");
System.exit(1);
}
}
}
ProgramTest.java:
package org.test.example;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.ExpectedSystemExit;
import org.junit.contrib.java.lang.system.SystemErrRule;
import org.junit.contrib.java.lang.system.SystemOutRule;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Program.class)
public class ProgramTest
{
/** Handles System.exit() calls. */
@Rule
private final ExpectedSystemExit exit = ExpectedSystemExit.none();
/** Handles System.err calls. */
@Rule
private final SystemErrRule err = new SystemErrRule().enableLog().muteForSuccessfulTests();
/** Handles System.out calls. */
@Rule
private final SystemOutRule out = new SystemOutRule().enableLog().muteForSuccessfulTests();
/**
* Verify server does not run when only a single valid command line
* parameter is provided.
*
* @throws Exception On error.
*/
@Test
public void testMainOneParameterPort() throws Exception
{
expectExit();
Program.main(new String[] { "-port" });
}
/**
* Helper function to setup System.exit() expectations
*/
private void expectExit()
{
exit.expectSystemExitWithStatus(1);
exit.checkAssertionAfterwards(() ->
{
assertFalse(out.getLog().isEmpty());
assertTrue(err.getLog().isEmpty());
});
}
}
Environment Configuration:
Windows 10 Pro x64 10.0.10586
Oracle JDK/JRE x64 1.8.0_66
Gradle 2.5
JUnit 4.12
System Rules 1.15.1
Mockito 1.10.19
PowerMock 1.6.4
PowerMock problem