What gets executed in the forked test-JVM when? Mocking System.exit


(Thorsten Schöning) #1

I have a custom daemon I would like to test the public static void main for, especially that System.exit provides the correct status codes depending on given args. I’ve read on SO multiple ways to intercept/mock calls to System.exit, but none of them work for me. The most promising are using JMockit, while mocking Runtime instead of System, and System Rules.

I’m pretty sure to get both running properly, because e.g. with JMockit and a mocked Runtime, its method getRuntime() returns null, which is not the case without mocking. But the result is always the same:

Unexpected exception thrown.
org.gradle.internal.remote.internal.MessageIOException: Could not read message from '/127.0.0.1:63646'.
		at org.gradle.internal.remote.internal.inet.SocketConnection.receive(SocketConnection.java:93)
		at org.gradle.internal.remote.internal.hub.MessageHub$ConnectionReceive.run(MessageHub.java:263)
		at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
		at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
		at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
		at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
		at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
		at java.lang.Thread.run(Thread.java:748)
Caused by: com.esotericsoftware.kryo.KryoException: java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
		at com.esotericsoftware.kryo.io.Input.fill(Input.java:148)
		at com.esotericsoftware.kryo.io.Input.require(Input.java:178)
		at com.esotericsoftware.kryo.io.Input.readByte(Input.java:295)
		at org.gradle.internal.serialize.kryo.KryoBackedDecoder.readByte(KryoBackedDecoder.java:80)
		at org.gradle.internal.remote.internal.hub.InterHubMessageSerializer$MessageReader.read(InterHubMessageSerializer.java:63)
		at org.gradle.internal.remote.internal.hub.InterHubMessageSerializer$MessageReader.read(InterHubMessageSerializer.java:52)
		at org.gradle.internal.remote.internal.inet.SocketConnection.receive(SocketConnection.java:80)
		... 7 more
Caused by: java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
		at sun.nio.ch.SocketDispatcher.read0(Native Method)
		at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
		at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
		at sun.nio.ch.IOUtil.read(IOUtil.java:192)
		at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
		at org.gradle.internal.remote.internal.inet.SocketConnection$SocketInputStream.read(SocketConnection.java:190)
		at com.esotericsoftware.kryo.io.Input.fill(Input.java:146)
		... 13 more
Unexpected exception thrown.
org.gradle.internal.remote.internal.MessageIOException: Could not write '/127.0.0.1:63646'.
		at org.gradle.internal.remote.internal.inet.SocketConnection.flush(SocketConnection.java:135)
		at org.gradle.internal.remote.internal.hub.MessageHub$ConnectionDispatch.run(MessageHub.java:325)
		at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
		at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
		at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
		at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
		at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
		at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
		at sun.nio.ch.SocketDispatcher.write0(Native Method)
		at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51)
		at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
		at sun.nio.ch.IOUtil.write(IOUtil.java:51)
		at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
		at org.gradle.internal.remote.internal.inet.SocketConnection$SocketOutputStream.writeWithNonBlockingRetry(SocketConnection.java:273)
		at org.gradle.internal.remote.internal.inet.SocketConnection$SocketOutputStream.writeBufferToChannel(SocketConnection.java:261)
		at org.gradle.internal.remote.internal.inet.SocketConnection$SocketOutputStream.flush(SocketConnection.java:255)        at org.gradle.internal.remote.internal.inet.SocketConnection.flush(SocketConnection.java:133)
		... 7 more

Because Gradle documents to launch a separate JVM for running tests, I thought there might be something special in this setup.

So, should JMockit and System Rules in theory work as with not executing in another JVM? What actually gets compiled and executed where when? Do I simply misunderstand the error message above? It reads to me like the second JVM for the tests is exited unexpectedly, which should not happen without System.exit.

Thanks!