Process() run() and waitForExit() get stuck when running mvn tests

I am developing a tool for myself using Swift and SwiftUI where I can retrieve student projects using git and then running Maven tests for the projects. The app is not sandboxed, since it is just for my personal use.

I use Process to launch git clone or git pull, and then get the commit log and parse the commit data to the app database. All this works just fine, I can see the app database table populated with repository commit data.

But when I do the same to execute Maven tests, and call

		try process.run()
		process.waitUntilExit()

The process never returns, unlike running git the same way. The only difference is the command executed and the arguments given to Process.

In the process view, I can see that there is a java child process running in my app, but it never (like in tens of minutes I have waited) completes. Running the same mvn test command...:

/opt/apache-maven-3.6.3/bin/mvn -Dstyle.color=never -Dtest=ReverseArrayRangeTests test

...from command line finishes in a couple of seconds.

While running the Maven command below executes just fine from Process:

/opt/apache-maven-3.6.3/bin/mvn -DskipTests -Dstyle.color=never package

In this case, I can see the output from the process while it builds the .jar package from the project.

Is there something special in running mvn test command, running Java in the child process, that it just does not work? Takes too much resources or something? macOS limiting what the child process can do, even though the app is not sandboxed?

I have also tried to Archive the app and run it outside of Xcode debugging, but that doesn't change anything.

My previous solution was to run the tests separately in Terminal using a shell script, saving the test results to a log file the app then read and parsed. But I'd like to do everything within the same GUI app, if possible. Any ideas?

Answered by DTS Engineer in 767164022

I use Process to launch git clone or git pull, and then get the commit log and parse the commit data to the app database.

Does that mean you’re trying to capture the process’s stdout and stderr? If so, there’s a known gotcha, found on all Unix-y systems, where small tests work but large tests deadlock. The pipe connecting the child’s stdout to you has a limited size. If you wait for the process to terminate without reading from that pipe, a small test will work because the pipe’s buffer is big enough to accommodate the test’s output, but a large test will fail as, when the pipe’s buffer fills up, the child process’s write blocks waiting for space.

I generally recommend that you avoid waitUntilExit() entirely, and instead do everything asynchronously. I have my own wrapper for that — see Running a Child Process with Standard Input and Output — but there are plenty of other options out their on the ’net.

Having said that, this isn’t the only cause of weird problems like this. Some programs are very dependent on their environment and thus behave differently when you run them as a child of your app versus as a child of the shell. My advice is that you fix the above problem first and then get back to us if you continue to have problems.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

I use Process to launch git clone or git pull, and then get the commit log and parse the commit data to the app database.

Does that mean you’re trying to capture the process’s stdout and stderr? If so, there’s a known gotcha, found on all Unix-y systems, where small tests work but large tests deadlock. The pipe connecting the child’s stdout to you has a limited size. If you wait for the process to terminate without reading from that pipe, a small test will work because the pipe’s buffer is big enough to accommodate the test’s output, but a large test will fail as, when the pipe’s buffer fills up, the child process’s write blocks waiting for space.

I generally recommend that you avoid waitUntilExit() entirely, and instead do everything asynchronously. I have my own wrapper for that — see Running a Child Process with Standard Input and Output — but there are plenty of other options out their on the ’net.

Having said that, this isn’t the only cause of weird problems like this. Some programs are very dependent on their environment and thus behave differently when you run them as a child of your app versus as a child of the shell. My advice is that you fix the above problem first and then get back to us if you continue to have problems.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks a lot! Will take a look and try this out.

Got it working, thanks a ton again! I just needed to add one parameter to the launch function, the currentDirectoryURL the process should execute in. Now it works great! Onwards to the next problem!

Process() run() and waitForExit() get stuck when running mvn tests
 
 
Q