Created
July 4, 2023 14:01
-
-
Save 0x6675636b796f75676974687562/18778df6a23900f95a458928e0832ad8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example | |
import org.junit.Assume.assumeTrue | |
import org.junit.AssumptionViolatedException | |
import org.junit.rules.TestRule | |
import org.junit.runner.Description | |
import org.junit.runners.model.Statement | |
import java.io.File.pathSeparatorChar | |
import java.nio.file.Path | |
import kotlin.io.path.Path | |
import kotlin.io.path.div | |
import kotlin.io.path.isDirectory | |
import kotlin.io.path.isExecutable | |
import kotlin.io.path.isRegularFile | |
import kotlin.io.path.name | |
/** | |
* Allows to ignore a JUnit 4 test if a specific executable is not found in | |
* `PATH`. Include an instance of this class into your test class as a _JUnit | |
* Rule_: | |
* | |
* ```kotlin | |
* @get:Rule | |
* val pathLookup = PathLookup() | |
* ``` | |
* | |
* Then, within your test method body, invoke [expectInPath] with a name of a | |
* specific executable (or multiple thereof), e.g.: | |
* | |
* ```kotlin | |
* @Test | |
* fun test() { | |
* pathLookup.expectInPath("clang") | |
* pathLookup.expectInPath("clang++") | |
* | |
* // Your test code here. | |
* } | |
* ``` | |
* | |
* The test will be ignored (i.e. throw an [AssumptionViolatedException]) if | |
* either `clang` or `clang++` is not present in `PATH`. | |
* | |
* On Windows, the `PATHEXT` environment variable is processed automatically to | |
* determine the list of extensions which indicate that a file is executable. | |
* | |
* @see expectInPath | |
*/ | |
class PathLookup : TestRule { | |
private val expectedExecutables = mutableListOf<Path>() | |
/** | |
* Adds [executableName] to the list of executables which are expected to be | |
* present in `PATH`. | |
*/ | |
fun expectInPath(executableName: String): Unit = | |
expectInPath(Path(executableName)) | |
/** | |
* Adds [executableName] to the list of executables which are expected to be | |
* present in `PATH`. | |
*/ | |
fun expectInPath(executableName: Path) { | |
expectedExecutables.add(executableName) | |
} | |
override fun apply(base: Statement, description: Description): Statement = | |
object : Statement() { | |
override fun evaluate() { | |
try { | |
base.evaluate() | |
} catch (t: Throwable) { | |
assumeAllExecutables() | |
throw t | |
} | |
assumeAllExecutables() | |
} | |
} | |
private fun assumeAllExecutables() { | |
val missingExecutables = expectedExecutables | |
.asSequence() | |
.filterNot { executable -> | |
executable.existsInPath | |
} | |
.toList() | |
assumeTrue("Executables not found in PATH: $missingExecutables", missingExecutables.isEmpty()) | |
} | |
private fun getenv(name: String, default: String): String = | |
getenv(name) ?: default | |
private fun getenv(name: String): String? = | |
when { | |
isWindows -> System.getenv() | |
.asSequence() | |
.firstOrNull { (key, _) -> | |
key.equals(name, ignoreCase = true) | |
} | |
?.value | |
else -> System.getenv(name) | |
} | |
private val pathEntries: Sequence<Path> | |
get() = | |
getenv("PATH") | |
?.splitToSequence(pathSeparatorChar) | |
?.filter(String::isNotEmpty) | |
?.map(::Path) | |
?.filter(Path::isDirectory) | |
?: emptySequence() | |
private val pathExtensions: Sequence<String> | |
get() = | |
when { | |
isWindows -> getenv("PATHEXT", ".com;.exe;.bat;.cmd;") | |
.splitToSequence(pathSeparatorChar) | |
.filter(String::isNotEmpty) | |
else -> emptySequence() | |
} | |
private val isWindows: Boolean | |
get() = | |
System.getProperty("os.name", "").startsWith("Windows ") | |
private val Path.existsInPath: Boolean | |
get() = | |
pathEntries | |
.flatMap { pathEntry -> | |
sequence { | |
yield(pathEntry / this@existsInPath) | |
yieldAll( | |
pathExtensions.map { ext -> | |
pathEntry / (this@existsInPath.name + ext) | |
} | |
) | |
} | |
} | |
.filter(Path::isRegularFile) | |
.any(Path::isExecutable) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment