Skip to content

Commit

Permalink
Merge pull request #29 from jaksonlin/unittest-context
Browse files Browse the repository at this point in the history
add unittest management context
  • Loading branch information
jaksonlin authored Dec 28, 2024
2 parents 9723c9c + 569f86a commit 5b55df7
Show file tree
Hide file tree
Showing 55 changed files with 3,086 additions and 31 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@

## 1.0.2 - 2024-10-12
- Bugfix: change plugin name introduce a bug in the code.

## 1.0.3 - 2024-11-14
- Add unit test annotation generation action.
- Add unit test annotation inspection.
9 changes: 7 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
alias(libs.plugins.changelog) // Gradle Changelog Plugin
alias(libs.plugins.qodana) // Gradle Qodana Plugin
alias(libs.plugins.kover) // Gradle Kover Plugin
alias(libs.plugins.serialize) // Gradle Serializer Plugin
}

group = providers.gradleProperty("pluginGroup").get()
Expand All @@ -29,7 +30,10 @@ intellij {
type.set(providers.gradleProperty("platformType"))

// Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file.
plugins.set(providers.gradleProperty("platformBundledPlugins").map { it.split(',').map(String::trim).filter(String::isNotEmpty) })
plugins.set(providers.gradleProperty("platformBundledPlugins")
.map { it.split(',').map(String::trim).filter(String::isNotEmpty) }
.map { it + listOf("git4idea") }
)
}

// Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog
Expand All @@ -41,6 +45,7 @@ dependencies {
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.18.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.18.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") // Use latest version
}

// Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin
Expand Down Expand Up @@ -124,6 +129,6 @@ tasks {
// The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3
// Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more:
// https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel
channels = providers.gradleProperty("pluginVersion").map { listOf(it.split('-').getOrElse(1) { "default" }.split('.').first()) }
channels = providers.gradleProperty("pluginVersion").map { listOf(it.substringAfter('-', "").substringBefore('.').ifEmpty { "default" }) }
}
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.jaksonlin.pitestintellij
pluginName = pitest-gradle
pluginRepositoryUrl = https://github.com/jaksonlin/pitest-gradle
# SemVer format -> https://semver.org
pluginVersion = 1.0.2
pluginVersion = 1.0.3

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 222
Expand All @@ -18,7 +18,7 @@ platformVersion = 2022.2
# Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP
platformPlugins =
# Example: platformBundledPlugins = com.intellij.java
platformBundledPlugins = com.intellij.java, com.intellij.gradle
platformBundledPlugins = com.intellij.java, com.intellij.gradle,com.intellij.java,git4idea

# Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion = 8.9
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ gradleIntelliJPlugin = { id = "org.jetbrains.intellij", version.ref = "gradleInt
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
qodana = { id = "org.jetbrains.qodana", version.ref = "qodana" }
serialize = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.github.jaksonlin.pitestintellij.actions

import com.github.jaksonlin.pitestintellij.commands.unittestannotations.GenerateAnnotationCommand
import com.github.jaksonlin.pitestintellij.context.CaseCheckContext
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil


class GenerateAnnotationCommandAction : AnAction() {

override fun actionPerformed(e: AnActionEvent) {
val psiMethodInfo = findMethodAtCaret(e) ?: return
val context = CaseCheckContext.create(psiMethodInfo.first, psiMethodInfo.second)
GenerateAnnotationCommand(e.project!!, context).execute()
}

private fun findMethodAtCaret(e: AnActionEvent): Pair<PsiMethod, PsiClass>? {
val project = e.project ?: return null
val editor = e.dataContext.getData(CommonDataKeys.EDITOR) ?: return null
val caret = e.dataContext.getData(CommonDataKeys.CARET) ?: return null
val elementAtCaret = PsiDocumentManager.getInstance(project)
.getPsiFile(editor.document)?.findElementAt(caret.offset) ?: return null
val method = PsiTreeUtil.getParentOfType(elementAtCaret, PsiMethod::class.java) ?: return null
val containingClass = PsiTreeUtil.getParentOfType(method, PsiClass::class.java) ?: return null
return method to containingClass
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.github.jaksonlin.pitestintellij.actions

import com.github.jaksonlin.pitestintellij.commands.unittestannotations.CheckAnnotationCommand
import com.github.jaksonlin.pitestintellij.context.CaseCheckContext
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil

class RunCaseAnnoationCheckAction : AnAction() {

override fun actionPerformed(e: AnActionEvent) {
val psiMethodInfo = findMethodAtCaret(e) ?: return
val context = CaseCheckContext.create(psiMethodInfo.first, psiMethodInfo.second)
CheckAnnotationCommand(e.project!!, context).execute()
}

private fun findMethodAtCaret(e: AnActionEvent): Pair<PsiMethod, PsiClass>? {
val project = e.project ?: return null
val editor = e.dataContext.getData(CommonDataKeys.EDITOR) ?: return null
val caret = e.dataContext.getData(CommonDataKeys.CARET) ?: return null
val elementAtCaret = PsiDocumentManager.getInstance(project)
.getPsiFile(editor.document)?.findElementAt(caret.offset) ?: return null
val method = PsiTreeUtil.getParentOfType(elementAtCaret, PsiMethod::class.java) ?: return null
val containingClass = PsiTreeUtil.getParentOfType(method, PsiClass::class.java) ?: return null
return method to containingClass
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.github.jaksonlin.pitestintellij.actions

import com.github.jaksonlin.pitestintellij.commands.unittestannotations.CheckAnnotationCommand
import com.github.jaksonlin.pitestintellij.context.CaseCheckContext
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.psi.JavaRecursiveElementVisitor
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil

class RunTestFileAnnoationCheckAction: AnAction() {

override fun actionPerformed(e: AnActionEvent) {
batchCheckAnnotation(e)
}

private fun batchCheckAnnotation(e: AnActionEvent){
val psiFile = e.dataContext.getData(CommonDataKeys.PSI_FILE)
val psiJavaFile = psiFile as PsiJavaFile

psiJavaFile.accept(object : JavaRecursiveElementVisitor() {
override fun visitMethod(method: PsiMethod) {
super.visitMethod(method)
// inspect the method annotations
val annotations = method.annotations
for (annotation in annotations) {
// inspect the annotation
val annotationName = annotation.qualifiedName
if (annotationName!=null && // to support junit 4 & 5, do not use regexp, as it will also match some beforeTest/afterTest annotations
(annotationName == "org.junit.Test" || annotationName == "org.junit.jupiter.api.Test" || annotationName == "Test")) {
val psiClass = PsiTreeUtil.getParentOfType(method, PsiClass::class.java) ?: return
val context = CaseCheckContext.create(method, psiClass)
CheckAnnotationCommand(e.project!!, context).execute()
break
}
}
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.github.jaksonlin.pitestintellij.annotations

import kotlinx.serialization.Contextual
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator

@Serializable
sealed class DefaultValue {
@Serializable
@SerialName("StringValue")
data class StringValue(val value: String) : DefaultValue()

@Serializable
@SerialName("StringListValue")
data class StringListValue(val value: List<String>) : DefaultValue()

@Serializable
@SerialName("NullValue")
object NullValue : DefaultValue()
}


@Serializable
data class AnnotationFieldConfig(
val name: String,
val type: AnnotationFieldType,
val required: Boolean = false,
val defaultValue: DefaultValue = DefaultValue.NullValue,
val validation: FieldValidation? = null,
val valueProvider: ValueProvider? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.jaksonlin.pitestintellij.annotations

import kotlinx.serialization.Serializable

@Serializable
enum class AnnotationFieldType {
STRING,
STRING_LIST
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.github.jaksonlin.pitestintellij.annotations

import com.github.jaksonlin.pitestintellij.context.UnittestCase


class AnnotationParser(private val schema: AnnotationSchema) {
private val validator = AnnotationValidator(schema)

fun parseAnnotation(annotationValues: Map<String, Any>): UnittestCase {
when (val result = validator.validate(annotationValues)) {
is AnnotationValidator.ValidationResult.Valid -> {
val parsedValues = schema.fields.associate { field ->
if (field.required) {
if (!annotationValues.containsKey(field.name)) {
throw IllegalArgumentException("Missing required field: ${field.name}")
}
if (annotationValues[field.name] == null) {
throw IllegalArgumentException("Required field cannot be null: ${field.name}")
}
} else {
if (!annotationValues.containsKey(field.name) || annotationValues[field.name] == null) {
return@associate field.name to field.defaultValue
}
}
val rawValue = annotationValues[field.name]
field.name to convertValue(rawValue, field)
}
return UnittestCase(parsedValues)
}
is AnnotationValidator.ValidationResult.Invalid -> {
throw IllegalArgumentException(
"Invalid annotation values:\n${result.errors.joinToString("\n")}"
)
}
}
}

private fun convertValue(value: Any?, field: AnnotationFieldConfig): Any? {
if (value == null) {
return when (val defaultValue = field.defaultValue) {
is DefaultValue.StringValue -> defaultValue.value
is DefaultValue.StringListValue -> defaultValue.value
DefaultValue.NullValue -> null
}
}

return when (field.type) {
AnnotationFieldType.STRING -> value as? String ?: field.defaultValue
AnnotationFieldType.STRING_LIST -> (value as? List<*>)?.mapNotNull { it as? String } ?: emptyList<String>()
}
}
}
Loading

0 comments on commit 5b55df7

Please sign in to comment.