[Kotlin] YT-issue commit handler: filter & tests

This commit adds 'fleet/plugins/kotlin' to the list
of paths to exclude from the check.

It also introduces tests covering filtering and
message validation.

GitOrigin-RevId: 3114df66be4ce73deb8a6483e4503b4f8164d135
This commit is contained in:
Andrei Klunnyi
2021-12-17 16:48:43 +00:00
committed by intellij-monorepo-bot
parent aa66a7591a
commit 97ad13d9df
3 changed files with 300 additions and 24 deletions
@@ -6,6 +6,7 @@
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="org.jetbrains.idea.devkit" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -63,5 +64,6 @@
<orderEntry type="module" module-name="intellij.platform.ide.util.netty" />
<orderEntry type="library" name="CGLIB" level="project" />
<orderEntry type="module" module-name="intellij.platform.bootstrap" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
</component>
</module>
@@ -13,26 +13,36 @@ import com.intellij.util.PairConsumer
import com.intellij.util.io.systemIndependentPath
import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.util.PsiUtil
import java.io.File
import kotlin.io.path.extension
import kotlin.io.path.isRegularFile
class KotlinPluginCommitMessageHandlerFactory : CheckinHandlerFactory() {
companion object {
private const val kotlinPluginPath = "plugins/kotlin/"
private val pathsToIgnore = setOf("/test/", "/testData/")
private val fileExtensionsNotToTrack = setOf("iml", "md")
private val commitMessageRegex = Regex(".*KTIJ-\\d+.*", RegexOption.DOT_MATCHES_ALL /* line breaks matter */)
}
override fun createHandler(panel: CheckinProjectPanel, commitContext: CommitContext): CheckinHandler =
YouTrackIssueCommitMessageHandler(panel)
private class YouTrackIssueCommitMessageHandler(private val checkinPanel: CheckinProjectPanel) : CheckinHandler() {
internal class YouTrackIssueCommitMessageHandler(private val checkinPanel: CheckinProjectPanel) : CheckinHandler() {
companion object {
const val HANDLER_ENABLED_KEY = "kotlin.commit.message.validation.enabled"
private const val HANDLER_ENABLED_KEY = "kotlin.commit.message.validation.enabled"
private const val KOTLIN_PLUGIN_PATH = "plugins/kotlin/"
private val pathsToIgnore = setOf("/test/", "/testData/", "/fleet/plugins/kotlin/")
private val fileExtensionsNotToTrack = setOf("iml", "md")
private val commitMessageRegex = Regex(".*KTIJ-\\d+.*", RegexOption.DOT_MATCHES_ALL /* line breaks matter */)
internal fun selectedFilesBelongToKotlinIdePlugin(files: Collection<File>): Boolean {
return files.asSequence()
.map { file -> file.toPath() }
.any { path ->
val siPath = path.systemIndependentPath
path.extension !in fileExtensionsNotToTrack
&& siPath.contains(KOTLIN_PLUGIN_PATH)
&& pathsToIgnore.none { siPath.contains(it) }
}
}
internal fun commitMessageIsCorrect(message: String): Boolean = message.matches(commitMessageRegex)
}
private val project = checkinPanel.project
@@ -52,7 +62,7 @@ class KotlinPluginCommitMessageHandlerFactory : CheckinHandlerFactory() {
if (!messageHandlerEnabled
|| !commitMessageShouldBeChecked()
|| checkinPanel.commitMessage.matches(commitMessageRegex)
|| commitMessageIsCorrect(checkinPanel.commitMessage)
) {
return ReturnResult.COMMIT // as is
}
@@ -73,18 +83,6 @@ class KotlinPluginCommitMessageHandlerFactory : CheckinHandlerFactory() {
}
private fun commitMessageShouldBeChecked(): Boolean =
PsiUtil.isIdeaProject(project) && selectedFilesBelongToKotlinIdePlugin()
private fun selectedFilesBelongToKotlinIdePlugin(): Boolean {
return checkinPanel.files.asSequence()
.map { file -> file.toPath() }
.any { path ->
val siPath = path.systemIndependentPath
path.isRegularFile()
&& path.extension !in fileExtensionsNotToTrack
&& siPath.contains(kotlinPluginPath)
&& pathsToIgnore.none { siPath.contains(it) }
}
}
PsiUtil.isIdeaProject(project) && selectedFilesBelongToKotlinIdePlugin(checkinPanel.files)
}
}
@@ -0,0 +1,276 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.commit
import org.jetbrains.idea.devkit.commit.KotlinPluginCommitMessageHandlerFactory.YouTrackIssueCommitMessageHandler
import org.junit.Test
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameter
import java.io.File
import java.nio.file.Paths
@RunWith(Enclosed::class)
class KotlinYouTrackIssueCommitMessageHandlerTest {
@RunWith(Parameterized::class)
class FilesBelongToPlugin {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun provideTestParameters(): Iterable<List<Pair<String, String>>> {
return listOf(
listOf(ULTIMATE_KOTLIN_PLUGIN to "File.kt"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.kt"),
listOf(ULTIMATE_KOTLIN_PLUGIN to "File.kts"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.kts"),
listOf(ULTIMATE_KOTLIN_PLUGIN to "File.java"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.java"),
listOf(ULTIMATE_KOTLIN_PLUGIN to "File.properties"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.properties"),
listOf(ULTIMATE_KOTLIN_PLUGIN to "File.html"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.html"),
listOf(ULTIMATE_KOTLIN_PLUGIN to "File.xml"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.xml"),
listOf(
COMMUNITY_KOTLIN_PLUGIN to "File.kt",
"not-important-dir-A" to "whatever-file",
"not-important-dir-B" to "whatever-file"
),
listOf(
"not-important-dir-A" to "whatever-file",
COMMUNITY_KOTLIN_PLUGIN to "File.kt",
"not-important-dir-B" to "whatever-file"
),
listOf(
"not-important-dir-A" to "whatever-file",
"not-important-dir-B" to "whatever-file",
COMMUNITY_KOTLIN_PLUGIN to "File.kt"
)
)
}
}
@Parameter
lateinit var files: List<Pair<String, String>>
@Test
fun testThatChecksForFileSet() {
val filesSet = files.map { fileAt(it.first, it.second) }
assert(YouTrackIssueCommitMessageHandler.selectedFilesBelongToKotlinIdePlugin(filesSet)) {
"The following set of files doesn't trigger the check: $filesSet"
}
}
}
@RunWith(Parameterized::class)
class FilesDoNotBelongToPlugin {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun provideTestParameters(): Iterable<List<Pair<String, String>>> {
return listOf(
// file path has no kotlin-plugin subdirectory
listOf("not-important-dir" to "File.kt"),
listOf(FLEET_KOTLIN_PLUGIN to "File.kt"),
// kotlin-plugin path contains subdirectories to exclude
listOf("$ULTIMATE_KOTLIN_PLUGIN/test/" to "File.kt"),
listOf("$ULTIMATE_KOTLIN_PLUGIN/testData/" to "File.kt"),
// file extensions to exclude
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.iml"),
listOf(COMMUNITY_KOTLIN_PLUGIN to "File.md"),
// multiple files, none matches
listOf(
"not-important-dir" to "File.kt",
FLEET_KOTLIN_PLUGIN to "File.kt",
"$ULTIMATE_KOTLIN_PLUGIN/test/" to "File.kt",
"$ULTIMATE_KOTLIN_PLUGIN/testData/" to "File.kt",
COMMUNITY_KOTLIN_PLUGIN to "File.iml",
COMMUNITY_KOTLIN_PLUGIN to "File.md"
)
)
}
}
@Parameter
lateinit var files: List<Pair<String, String>>
@Test
fun testThatIgnoresFileSet() {
val filesSet = files.map { fileAt(it.first, it.second) }
assert(!YouTrackIssueCommitMessageHandler.selectedFilesBelongToKotlinIdePlugin(filesSet)) {
"The following set of files triggered the check: $filesSet"
}
}
}
@RunWith(Parameterized::class)
class ValidCommitMessages {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun provideTestParameters(): Iterable<String> {
return listOf(
"KTIJ-123",
"(KTIJ-123)",
"[KTIJ-123]",
"{KTIJ-123}",
"'KTIJ-123'",
"`KTIJ-123`",
"\"KTIJ-123\"",
"KTIJ-123 ",
" KTIJ-123",
" KTIJ-123 ",
"KTIJ-123 header",
"Header KTIJ-123",
"Header KTIJ-123 header",
"""
Header KTIJ-123 header
Body-line-1
Body-line-N
""".trimIndent(),
"""
Header KTIJ-123 header
Body-line-1
Body-line-N
^KTIJ-123 fixed
""".trimIndent(),
"""
Header
Body-line-1
Body-line-N
^KTIJ-123 fixed
""".trimIndent(),
"""
Header
Body-line-1
Body-line-N
#KTIJ-123 fixed
""".trimIndent(),
"""
Header
Body-line-1
Body-line-N
Relates to #KTIJ-123
""".trimIndent()
)
}
}
@Parameter
lateinit var commitMessage: String
@Test
fun testThatCommitMessageIsValid() {
assert(YouTrackIssueCommitMessageHandler.commitMessageIsCorrect(commitMessage)) {
"The following commit message was considered invalid: $commitMessage"
}
}
}
@RunWith(Parameterized::class)
class InvalidCommitMessages {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun provideTestParameters(): Iterable<String> {
return listOf(
"",
"KTIJ",
"KTIJ-",
"KTIJ-one",
"KT-123",
"IDEA-123",
"WHATEVER-123",
"Header KTIJ",
"Header KTIJ header",
"""
Header
Body-line-1
Body-line-N
""".trimIndent(),
"""
Header
Body-line-1
Body-line-N
^IDEA-123 fixed
""".trimIndent(),
"""
Header
Body-line-1
Body-line-N
#IDEA-123 fixed
""".trimIndent(),
"""
Header
Body-line-1
Body-line-N
Relates to #IDEA-123
""".trimIndent(),
)
}
}
@Parameter
lateinit var commitMessage: String
@Test
fun testThatCommitMessageIsValid() {
assert(!YouTrackIssueCommitMessageHandler.commitMessageIsCorrect(commitMessage)) {
"The following commit message was considered as valid: $commitMessage"
}
}
}
}
val tempDir: String = System.getProperty("java.io.tmpdir")
const val ULTIMATE_KOTLIN_PLUGIN = "plugins/kotlin/package/"
const val COMMUNITY_KOTLIN_PLUGIN = "community/plugins/kotlin/package/"
const val FLEET_KOTLIN_PLUGIN = "fleet/plugins/kotlin/package/"
private fun fileAt(dirPath: String, fileName: String): File {
return Paths.get(tempDir, *dirPath.split("/").toTypedArray(), fileName).toFile()
}