Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support baseline-include-absent flag in baseline cli #20

Merged
merged 2 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions baseline-cli/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
plugins {

Check warning on line 1 in baseline-cli/build.gradle.kts

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Check dependency licenses

Transitive dependency: 'kotlin-stdlib-jdk8' ('1.9.10') licenses cannot be recognized

Check warning on line 1 in baseline-cli/build.gradle.kts

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Check dependency licenses

Transitive dependency: 'kotlin-stdlib-jdk7' ('1.9.10') licenses cannot be recognized
id("qodana-sarif.common-conventions")
alias(libs.plugins.shadow)
}
Expand All @@ -6,6 +6,7 @@
dependencies {
implementation(projects.sarif)
implementation(libs.gson)
implementation(libs.clikt)
}

application {
Expand Down
25 changes: 12 additions & 13 deletions baseline-cli/src/main/kotlin/BaselineCli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ import java.nio.file.Path
import java.nio.file.Paths

object BaselineCli {
fun process(map: Map<String, String>, cliPrinter: (String) -> Unit, errPrinter: (String) -> Unit): Int {
val sarifPath = map["sarifReport"]!!
val baselinePath = map["baselineReport"]
val failThreshold = map["failThreshold"]?.toIntOrNull()
if (!Files.exists(Paths.get(sarifPath))) {
fun process(options: BaselineOptions, cliPrinter: (String) -> Unit, errPrinter: (String) -> Unit): Int {
if (!Files.exists(Paths.get(options.sarifPath))) {
errPrinter("Please provide a valid SARIF report path")
return ERROR_EXIT
}
val sarifReport = try {
SarifUtil.readReport(Paths.get(sarifPath))
SarifUtil.readReport(Paths.get(options.sarifPath))
} catch (e: Exception) {
errPrinter("Error reading SARIF report: ${e.message}")
return ERROR_EXIT
Expand All @@ -30,18 +27,19 @@ object BaselineCli {
RuleUtil.findRuleDescriptor(sarifReport, id)?.shortDescription?.text ?: id
}
val printer = CommandLineResultsPrinter(simpleMemoize(resolveInspectionName), cliPrinter)
return if (baselinePath != null) {
return if (options.baselinePath != null) {
compareBaselineThreshold(
sarifReport,
Paths.get(sarifPath),
Paths.get(baselinePath),
failThreshold,
Paths.get(options.sarifPath),
Paths.get(options.baselinePath),
options.failThreshold,
options.includeAbsent,
printer,
cliPrinter,
errPrinter
)
} else {
compareThreshold(sarifReport, Paths.get(sarifPath), failThreshold, printer, cliPrinter, errPrinter)
compareThreshold(sarifReport, Paths.get(options.sarifPath), options.failThreshold, printer, cliPrinter, errPrinter)
}
}

Expand Down Expand Up @@ -91,6 +89,7 @@ object BaselineCli {
sarifPath: Path,
baselinePath: Path,
failThreshold: Int?,
includeAbsent: Boolean,
printer: CommandLineResultsPrinter,
cliPrinter: (String) -> Unit,
errPrinter: (String) -> Unit
Expand All @@ -106,8 +105,8 @@ object BaselineCli {
errPrinter("Error reading baseline report: ${e.message}")
return ERROR_EXIT
}
val baselineCalculation = BaselineCalculation.compare(sarifReport, baseline, BaselineCalculation.Options())
printer.printResultsWithBaselineState(sarifReport.runs.first().results, false)
val baselineCalculation = BaselineCalculation.compare(sarifReport, baseline, BaselineCalculation.Options(includeAbsent))
printer.printResultsWithBaselineState(sarifReport.runs.first().results, includeAbsent)
val invocation = processResultCount(baselineCalculation.newResults, failThreshold, cliPrinter, errPrinter)
sarifReport.runs.first().invocations = listOf(invocation)
SarifUtil.writeReport(sarifPath, sarifReport)
Expand Down
44 changes: 22 additions & 22 deletions baseline-cli/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import BaselineCli.process
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.int
import kotlin.system.exitProcess

const val THRESHOLD_EXIT = 255
const val ERROR_EXIT = 1

fun main(args: Array<String>) {
val map = mutableMapOf<String, String>()

var i = 0
while (i < args.size) {
when (args[i]) {
"-r" -> map["sarifReport"] = args.getOrNull(i + 1) ?: printUsageAndExit()
"-b" -> map["baselineReport"] = args.getOrNull(i + 1) ?: printUsageAndExit()
"-f" -> map["failThreshold"] = args.getOrNull(i + 1) ?: printUsageAndExit()
}
i += 2
}

if (!map.contains("sarifReport")) {
printUsageAndExit()
class BaselineCommand : CliktCommand() {
private val sarifReport: String by option("-r", help = "Sarif report path").required()
private val baselineReport: String? by option("-b", help = "Baseline report path")
private val baselineIncludeAbsent: Boolean by option("-i", help = "Baseline include absent status").flag()
private val failThreshold: Int? by option("-f", help = "Fail threshold").int()
override fun run() {
val ret = process(
BaselineOptions(sarifReport, baselineReport, failThreshold, baselineIncludeAbsent),
{ println(it) },
{ System.err.println(it) })
exitProcess(ret)
}

val ret = process(map, { println(it) }, { System.err.println(it) })
exitProcess(ret)
}

fun printUsageAndExit(): Nothing {
println("Usage: -r <sarif_report> [-b <baseline_report>] [-f <fail_threshold>]")
exitProcess(1)
}
data class BaselineOptions(val sarifPath: String,
val baselinePath: String? = null,
val failThreshold: Int? = null,
val includeAbsent: Boolean = false)

fun main(args: Array<String>) = BaselineCommand().main(args)
105 changes: 40 additions & 65 deletions baseline-cli/src/test/kotlin/BaselineCliTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,18 @@ import kotlin.io.path.Path

class BaselineCliTest {

private val sarif = run {
val tmp = Files.createTempFile(null, ".sarif")
Files.copy(Path("src/test/resources/report.equal.sarif.json"), tmp, StandardCopyOption.REPLACE_EXISTING)
tmp.toFile().also(File::deleteOnExit).absolutePath
}
private val sarif = copySarifFromResources("report.equal.sarif.json")
private val emptySarif = copySarifFromResources("empty.sarif.json")
private val corruptedSarif = Path("src/test/resources/corrupted.sarif.json").toString()

private val stdout = StringBuilder()
private val stderr = StringBuilder()

@Test
fun `test when sarifReport does not exist in the provided path`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = "nonExistentPath.sarif"
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions("nonExistentPath.sarif"), stdout::append, stderr::append)
}

// Assert
Expand All @@ -36,14 +29,9 @@ class BaselineCliTest {

@Test
fun `test when there is an error reading sarifReport`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = Path("src/test/resources/corrupted.sarif.json").toString()
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(corruptedSarif), stdout::append, stderr::append)
}

// Assert
Expand All @@ -53,15 +41,9 @@ class BaselineCliTest {

@Test
fun `test when baselineReport path is provided and file does not exist`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = sarif
this["baselineReport"] = "nonExistentBaselineReport.sarif"
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(sarif, "nonExistentBaselineReport.sarif"), stdout::append, stderr::append)
}

// Assert
Expand All @@ -71,15 +53,9 @@ class BaselineCliTest {

@Test
fun `test when there is a error reading baselineReport`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = sarif
this["baselineReport"] = Path("src/test/resources/corrupted.sarif.json").toString()
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(sarif, corruptedSarif), stdout::append, stderr::append)
}

// Assert
Expand All @@ -89,14 +65,9 @@ class BaselineCliTest {

@Test
fun `test when failThreshold is not present and results count is less than the failThreshold default value`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = sarif
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(sarif), stdout::append, stderr::append)
}

// Assert
Expand All @@ -106,15 +77,9 @@ class BaselineCliTest {

@Test
fun `test when failThreshold is provided and results count in sarifReport is more than failThreshold`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = sarif
this["failThreshold"] = "0"
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(sarif, failThreshold = 0), stdout::append, stderr::append)
}

// Assert
Expand All @@ -124,16 +89,9 @@ class BaselineCliTest {

@Test
fun `test when failThreshold is provided and newResults count in baselineCalculation is more than failThreshold`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = sarif
this["baselineReport"] = Path("src/test/resources/empty.sarif.json").toString()
this["failThreshold"] = "1"
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(sarif, emptySarif, failThreshold = 1), stdout::append, stderr::append)
}

// Assert
Expand All @@ -143,15 +101,9 @@ class BaselineCliTest {

@Test
fun `test when failThreshold is not provided or is less than newResults count in baselineCalculation`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = sarif
this["baselineReport"] = Path("src/test/resources/empty.sarif.json").toString()
}

// Act
val exitCode = assertDoesNotThrow {
BaselineCli.process(map, stdout::append, stderr::append)
BaselineCli.process(BaselineOptions(sarif, emptySarif), stdout::append, stderr::append)
}

// Assert
Expand All @@ -161,17 +113,40 @@ class BaselineCliTest {

@Test
fun `test when rule description is available`() {
// Arrange
val map = mutableMapOf<String, String>().apply {
this["sarifReport"] = Path("src/test/resources/report.with-description.sarif.json").toString()
}

// Act
assertDoesNotThrow { BaselineCli.process(map, stdout::append, stderr::append) }
assertDoesNotThrow { BaselineCli.process(BaselineOptions(Path("src/test/resources/report.with-description.sarif.json").toString()), stdout::append, stderr::append) }

// Assert
assertTrue(stdout.contains("Result of method call ignored")) // the unresolved ID
assertFalse(stdout.contains("ResultOfMethodCallIgnored")) // the unresolved ID
}

@Test
fun `test include absent true`() {
// Act
assertDoesNotThrow { BaselineCli.process(BaselineOptions(emptySarif, sarif, includeAbsent = true), stdout::append, stderr::append) }

// Assert
assertTrue(stdout.contains("ABSENT: 2"))
val content = File(emptySarif).readText(charset("UTF-8"))
assertTrue(content.contains("absent"))
}

@Test
fun `test include absent false`() {
// Act
assertDoesNotThrow { BaselineCli.process(BaselineOptions(emptySarif, sarif, includeAbsent = false), stdout::append, stderr::append) }

// Assert
assertFalse(stdout.contains("ABSENT: 2"))
val content = File(emptySarif).readText(charset("UTF-8"))
assertFalse(content.contains("absent"))
}


private fun copySarifFromResources(name: String) = run {
val tmp = Files.createTempFile(null, ".sarif")
Files.copy(Path("src/test/resources/$name"), tmp, StandardCopyOption.REPLACE_EXISTING)
tmp.toFile().also(File::deleteOnExit).absolutePath
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
dokka = "1.9.10"
shadow = "8.1.1"
gson = "2.8.9"
clikt = "4.3.0"

[plugins]
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
Expand All @@ -11,3 +12,4 @@ dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
[libraries]
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
dokkaBase = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" }
clikt = { module = "com.github.ajalt.clikt:clikt", version.ref = "clikt" }
2 changes: 2 additions & 0 deletions qodana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ exclude:
- name: All
paths:
- sarif/src/test/resources/
dependencyIgnores:
- name: "colormath-jvm"
Loading