Skip to content

Commit

Permalink
Add baseline functionality to ktlint (pinterest#707)
Browse files Browse the repository at this point in the history
* Add baseline functionality to ktlint

* Fix up to use add reporter so that no other reporters are overwritten

* Fix ktlint issues

* Add baseline reporter

* Add relative layout comparison for easier to use baseline file on VCS and add in tests for baseline functionality

* Make baseline platform agnostic

* Make tests file system agnostic

* Add baseline

* Cleanup path routing

* Fix indentation issues

* Fix after rebase
  • Loading branch information
joshafeinberg authored Nov 18, 2020
1 parent 48a420e commit dbfbfc7
Show file tree
Hide file tree
Showing 18 changed files with 462 additions and 10 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ $ ktlint --reporter=plain?group_by_file
# print style violations as usual + create report in checkstyle format
$ ktlint --reporter=plain --reporter=checkstyle,output=ktlint-report-in-checkstyle-format.xml

# check against a baseline file
$ ktlint --baseline=ktlint-baseline.xml

# install git hook to automatically check files for style violations on commit
# Run "ktlint installGitPrePushHook" if you wish to run ktlint on push instead
$ ktlint installGitPreCommitHook
Expand Down Expand Up @@ -288,6 +291,8 @@ task ktlint(type: JavaExec, group: "verification") {
args "src/**/*.kt"
// to generate report in checkstyle format prepend following args:
// "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
// to add a baseline to check against prepend following args:
// "--baseline=ktlint-baseline.xml"
// see https://github.com/pinterest/ktlint#usage for more
}
check.dependsOn ktlint
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ task ktlint(type: JavaExec, group: LifecycleBasePlugin.VERIFICATION_GROUP) {
description = "Check Kotlin code style."
classpath = configurations.ktlint
main = 'com.pinterest.ktlint.Main'
args '*/src/**/*.kt'
args '*/src/**/*.kt', '--baseline=ktlint-baseline.xml'
}

allprojects {
Expand Down
11 changes: 11 additions & 0 deletions ktlint-baseline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<baseline version="1.0">
<file name="ktlint/src/test/resources/TestBaselineExtraErrorFile.kt">
<error line="1" column="34" source="no-empty-class-body" />
<error line="2" column="1" source="no-blank-line-before-rbrace" />
</file>
<file name="ktlint/src/test/resources/TestBaselineFile.kt">
<error line="1" column="24" source="no-empty-class-body" />
<error line="2" column="1" source="no-blank-line-before-rbrace" />
</file>
</baseline>
12 changes: 12 additions & 0 deletions ktlint-reporter-baseline/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'ktlint-publication'
}

dependencies {
implementation project(':ktlint-core')
implementation deps.kotlin.stdlib

testImplementation deps.junit
testImplementation deps.assertj
}
4 changes: 4 additions & 0 deletions ktlint-reporter-baseline/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GROUP=com.pinterest.ktlint
POM_NAME=ktlint-reporter-baseline
POM_ARTIFACT_ID=ktlint-reporter-baseline
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.pinterest.ktlint.reporter.baseline

import com.pinterest.ktlint.core.LintError
import com.pinterest.ktlint.core.Reporter
import java.io.File
import java.io.PrintStream
import java.nio.file.Paths
import java.util.ArrayList
import java.util.concurrent.ConcurrentHashMap

class BaselineReporter(val out: PrintStream) : Reporter {

private val acc = ConcurrentHashMap<String, MutableList<LintError>>()

override fun onLintError(file: String, err: LintError, corrected: Boolean) {
if (!corrected) {
acc.getOrPut(file) { ArrayList<LintError>() }.add(err)
}
}

override fun afterAll() {
out.println("""<?xml version="1.0" encoding="utf-8"?>""")
out.println("""<baseline version="1.0">""")
for ((file, errList) in acc.entries.sortedBy { it.key }) {
val fileName = try {
val rootPath = Paths.get("").toAbsolutePath()
val filePath = Paths.get(file)
rootPath.relativize(filePath).toString().replace(File.separatorChar, '/')
} catch (e: IllegalArgumentException) {
file
}
out.println(""" <file name="${fileName.escapeXMLAttrValue()}">""")
for ((line, col, ruleId, _) in errList) {
out.println(
""" <error line="$line" column="$col" source="$ruleId" />"""
)
}
out.println(""" </file>""")
}
out.println("""</baseline>""")
}

private fun String.escapeXMLAttrValue() =
this.replace("&", "&amp;").replace("\"", "&quot;").replace("'", "&apos;")
.replace("<", "&lt;").replace(">", "&gt;")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.pinterest.ktlint.reporter.baseline

import com.pinterest.ktlint.core.Reporter
import com.pinterest.ktlint.core.ReporterProvider
import java.io.PrintStream

class BaselineReporterProvider : ReporterProvider {
override val id: String = "baseline"
override fun get(out: PrintStream, opt: Map<String, String>): Reporter = BaselineReporter(out)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.pinterest.ktlint.reporter.baseline.BaselineReporterProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.pinterest.ktlint.reporter.baseline

import com.pinterest.ktlint.core.LintError
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.nio.file.Paths
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class BaselineReporterTest {

@Test
fun testReportGeneration() {
val basePath = Paths.get("").toAbsolutePath()
val out = ByteArrayOutputStream()
val reporter = BaselineReporter(PrintStream(out, true))
reporter.onLintError(
"$basePath/one-fixed-and-one-not.kt",
LintError(
1, 1, "rule-1",
"<\"&'>"
),
false
)
reporter.onLintError(
"$basePath/one-fixed-and-one-not.kt",
LintError(
2, 1, "rule-2",
"And if you see my friend"
),
true
)

reporter.onLintError(
"$basePath/two-not-fixed.kt",
LintError(
1, 10, "rule-1",
"I thought I would again"
),
false
)
reporter.onLintError(
"$basePath/two-not-fixed.kt",
LintError(
2, 20, "rule-2",
"A single thin straight line"
),
false
)

reporter.onLintError(
"$basePath/all-corrected.kt",
LintError(
1, 1, "rule-1",
"I thought we had more time"
),
true
)
reporter.afterAll()
assertThat(String(out.toByteArray())).isEqualTo(
"""
<?xml version="1.0" encoding="utf-8"?>
<baseline version="1.0">
<file name="one-fixed-and-one-not.kt">
<error line="1" column="1" source="rule-1" />
</file>
<file name="two-not-fixed.kt">
<error line="1" column="10" source="rule-1" />
<error line="2" column="20" source="rule-2" />
</file>
</baseline>
""".trimStart().replace("\n", System.lineSeparator())
)
}
}
1 change: 1 addition & 0 deletions ktlint/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ publishing.publications.named("maven").configure {

dependencies {
implementation project(':ktlint-core')
implementation project(':ktlint-reporter-baseline')
implementation project(':ktlint-reporter-checkstyle')
implementation project(':ktlint-reporter-json')
implementation project(':ktlint-reporter-html')
Expand Down
41 changes: 32 additions & 9 deletions ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ import com.pinterest.ktlint.internal.GitPrePushHookSubCommand
import com.pinterest.ktlint.internal.JarFiles
import com.pinterest.ktlint.internal.KtlintVersionProvider
import com.pinterest.ktlint.internal.PrintASTSubCommand
import com.pinterest.ktlint.internal.containsLintError
import com.pinterest.ktlint.internal.fileSequence
import com.pinterest.ktlint.internal.formatFile
import com.pinterest.ktlint.internal.lintFile
import com.pinterest.ktlint.internal.loadBaseline
import com.pinterest.ktlint.internal.loadRulesets
import com.pinterest.ktlint.internal.location
import com.pinterest.ktlint.internal.printHelpOrVersionUsage
import com.pinterest.ktlint.internal.relativeRoute
import com.pinterest.ktlint.internal.toFilesURIList
import com.pinterest.ktlint.reporter.plain.internal.Color
import java.io.File
Expand Down Expand Up @@ -212,6 +215,12 @@ class KtlintCommandLine {
)
var experimental: Boolean = false

@Option(
names = ["--baseline"],
description = ["Defines a baseline file to check against"]
)
private var baseline: String = ""

@Parameters(hidden = true)
private var patterns = ArrayList<String>()

Expand All @@ -224,8 +233,14 @@ class KtlintCommandLine {

val start = System.currentTimeMillis()

val baselineResults = loadBaseline(baseline)
val ruleSetProviders = rulesets.loadRulesets(experimental, debug)
val reporter = loadReporter()
var reporter = loadReporter()
if (baselineResults.baselineGenerationNeeded) {
val baselineReporter = ReporterTemplate("baseline", null, emptyMap(), baseline)
val reporterProviderById = loadReporters(emptyList())
reporter = Reporter.from(reporter, baselineReporter.toReporter(reporterProviderById))
}
val userData = listOfNotNull(
"android" to android.toString(),
if (disabledRules.isNotBlank()) "disabled_rules" to disabledRules else null
Expand All @@ -235,7 +250,7 @@ class KtlintCommandLine {
if (stdin) {
lintStdin(ruleSetProviders, userData, reporter)
} else {
lintFiles(ruleSetProviders, userData, reporter)
lintFiles(ruleSetProviders, userData, baselineResults.baselineRules, reporter)
}
reporter.afterAll()
if (debug) {
Expand All @@ -253,6 +268,7 @@ class KtlintCommandLine {
private fun lintFiles(
ruleSetProviders: Map<String, RuleSetProvider>,
userData: Map<String, String>,
baseline: Map<String, List<LintError>>?,
reporter: Reporter
) {
patterns.fileSequence()
Expand All @@ -263,7 +279,8 @@ class KtlintCommandLine {
file.path,
file.readText(),
ruleSetProviders,
userData
userData,
baseline?.get(file.relativeRoute)
)
}
}
Expand All @@ -281,7 +298,8 @@ class KtlintCommandLine {
KtLint.STDIN_FILE,
String(System.`in`.readBytes()),
ruleSetProviders,
userData
userData,
null
),
reporter
)
Expand Down Expand Up @@ -324,7 +342,8 @@ class KtlintCommandLine {
fileName: String,
fileContent: String,
ruleSetProviders: Map<String, RuleSetProvider>,
userData: Map<String, String>
userData: Map<String, String>,
baselineErrors: List<LintError>?
): List<LintErrorWithCorrectionInfo> {
if (debug) {
val fileLocation = if (fileName != KtLint.STDIN_FILE) File(fileName).location(relative) else fileName
Expand All @@ -342,8 +361,10 @@ class KtlintCommandLine {
debug
) { err, corrected ->
if (!corrected) {
result.add(LintErrorWithCorrectionInfo(err, corrected))
tripped.set(true)
if (baselineErrors == null || !baselineErrors.containsLintError(err)) {
result.add(LintErrorWithCorrectionInfo(err, corrected))
tripped.set(true)
}
}
}
} catch (e: Exception) {
Expand All @@ -368,8 +389,10 @@ class KtlintCommandLine {
editorConfigPath,
debug
) { err ->
result.add(LintErrorWithCorrectionInfo(err, false))
tripped.set(true)
if (baselineErrors == null || !baselineErrors.containsLintError(err)) {
result.add(LintErrorWithCorrectionInfo(err, false))
tripped.set(true)
}
}
} catch (e: Exception) {
result.add(LintErrorWithCorrectionInfo(e.toLintError(), false))
Expand Down
Loading

0 comments on commit dbfbfc7

Please sign in to comment.