Skip to content

Commit

Permalink
Add ProcessUtil
Browse files Browse the repository at this point in the history
Similar to `Util.sh`, but with Kotlin and coroutines
  • Loading branch information
Lipen committed Jan 29, 2025
1 parent ddbea5d commit e899210
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.ets.utils

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.Reader

object ProcessUtil {
fun run(command: List<String>, input: String? = null): String {

Check warning on line 26 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L26

Added line #L26 was not covered by tests
val reader = input?.reader() ?: "".reader()
return run(command, reader)

Check warning on line 28 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L28

Added line #L28 was not covered by tests
}

fun run(command: List<String>, input: Reader): String {
val process = ProcessBuilder(command).start()
return communicate(process, input)

Check warning on line 33 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L32-L33

Added lines #L32 - L33 were not covered by tests
}

private fun communicate(process: Process, input: Reader): String {
val stdout = StringBuilder()
val stderr = StringBuilder()

Check warning on line 38 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L37-L38

Added lines #L37 - L38 were not covered by tests

val scope = CoroutineScope(Dispatchers.IO)

Check warning on line 40 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L40

Added line #L40 was not covered by tests

// Handle process input
val stdinJob = scope.launch {
process.outputWriter().use { writer ->
input.copyTo(writer)
}
}

Check warning on line 47 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L44-L47

Added lines #L44 - L47 were not covered by tests

// Launch output capture coroutines
val stdoutJob = scope.launch {
process.inputReader().useLines { lines ->
lines.forEach { stdout.appendLine(it) }
}
}

Check warning on line 54 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L51-L54

Added lines #L51 - L54 were not covered by tests
val stderrJob = scope.launch {
process.errorReader().useLines { lines ->
lines.forEach { stderr.appendLine(it) }
}
}

Check warning on line 59 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L56-L59

Added lines #L56 - L59 were not covered by tests

// Wait for completion
val exitCode = process.waitFor()
runBlocking {
stdinJob.join()
stdoutJob.join()
stderrJob.join()
}

Check warning on line 67 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L62-L67

Added lines #L62 - L67 were not covered by tests

if (exitCode != 0) {
throw ProcessException(
"Process failed with exit code $exitCode",
exitCode,
stderr.toString()

Check warning on line 73 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L70-L73

Added lines #L70 - L73 were not covered by tests
)
}

return stdout.toString()

Check warning on line 77 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L77

Added line #L77 was not covered by tests
}

class ProcessException(

Check warning on line 80 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L80

Added line #L80 was not covered by tests
message: String,
val exitCode: Int,
val errorOutput: String,
) : RuntimeException(message)

Check warning on line 84 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L82-L84

Added lines #L82 - L84 were not covered by tests
}

fun main() {
// Note: `ls -l /bin/` has big enough output to demonstrate the necessity
// of separate output capture threads/coroutines.
val output = ProcessUtil.run(listOf("ls", "-l", "/bin/"))
println(output)
}

Check warning on line 92 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ProcessUtil.kt#L90-L92

Added lines #L90 - L92 were not covered by tests

0 comments on commit e899210

Please sign in to comment.