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

Add baseline functionality to ktlint #707

Merged
merged 11 commits into from
Nov 18, 2020
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