Skip to content

Introduce custom JavaDoc tags #565 #585

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

Merged
merged 29 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e88bd65
Introduce custom plugin's JavaDoc tags #565
onewhl Jul 22, 2022
43c4cf0
Render UtBot custom JavaDoc tags correctly #565
onewhl Jul 25, 2022
760594f
Add an option to generate summaries using custom JavaDoc tags #565
onewhl Jul 26, 2022
050550f
Fill value of utbot.iterates tag #565
onewhl Jul 27, 2022
ca86976
Collect info about Invoke, Iterate, and Return sections #565
onewhl Jul 28, 2022
d77e739
Review fixes
onewhl Aug 1, 2022
6ae1157
Add unit tests for summaries with custom JavaDoc tags #565
onewhl Aug 2, 2022
100e3df
Fix after rebasing
onewhl Aug 3, 2022
8d4063b
Add summary tests for MinStack #565
onewhl Aug 3, 2022
6d01afe
Fix broken tests
onewhl Aug 4, 2022
d03ad76
Add <pre> tag only in case when custom javadoc tags are not used
onewhl Aug 4, 2022
74c5822
Use a full exception name instead of simple name to build inline link…
onewhl Aug 6, 2022
7e4aaf9
Minor refactoring
onewhl Aug 6, 2022
92eb15d
Minor refactoring: avoid code duplication
onewhl Aug 6, 2022
cf9b60f
Add DocCustomTagStatement and CgCustomTagStatement
onewhl Aug 6, 2022
9c14eb0
Refactored code to avoid code duplication
onewhl Aug 6, 2022
6dce850
Fix tests: add full name for classes
onewhl Aug 6, 2022
7800d33
Add JUnit extension to control USE_CUSTOM_TAGS setting
onewhl Aug 6, 2022
d003962
Move useCustomJavaDocTags to UtSettings, make useFuzzing true
onewhl Aug 7, 2022
f8e6764
Remove unused import and fix broken tests
onewhl Aug 7, 2022
321bf0d
Fix broken tests
onewhl Aug 8, 2022
a52fb20
Add comments, remove unused method
onewhl Aug 8, 2022
83f4e71
Review fixes: fixed formatting, removed redundant types
onewhl Aug 9, 2022
abe7ca6
Review fixes: fixed formatting, removed useless overriding methods
onewhl Aug 15, 2022
475cae5
Review fixes: extracted method, polished code
onewhl Aug 15, 2022
99b06ad
fix after rebasing
onewhl Aug 17, 2022
21a7035
fix after rebasing
onewhl Aug 23, 2022
4951b6f
review fixes
onewhl Aug 23, 2022
bd616a9
fix rendering after updating to idea 2022.1. now we don't need to gen…
onewhl Aug 23, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ object UtSettings {
var testName by getBooleanProperty(true)
var testDisplayName by getBooleanProperty(true)

/**
* Generate summaries using plugin's custom JavaDoc tags.
*/
var useCustomJavaDocTags by getBooleanProperty(false)

/**
* Enable the machine learning module to generate summaries for methods under test.
* True by default.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,9 @@ class DocPreTagStatement(content: List<DocStatement>) : DocTagStatement(content)
override fun hashCode(): Int = content.hashCode()
}

data class DocCustomTagStatement(val statements: List<DocStatement>) : DocTagStatement(statements) {
override fun toString(): String = content.joinToString(separator = "")
}

open class DocClassLinkStmt(val className: String) : DocStatement() {
override fun toString(): String = className
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.DocClassLinkStmt
import org.utbot.framework.plugin.api.DocCodeStmt
import org.utbot.framework.plugin.api.DocCustomTagStatement
import org.utbot.framework.plugin.api.DocMethodLinkStmt
import org.utbot.framework.plugin.api.DocPreTagStatement
import org.utbot.framework.plugin.api.DocRegularStmt
Expand Down Expand Up @@ -52,6 +53,7 @@ interface CgElement {
is CgMultilineComment -> visit(element)
is CgDocumentationComment -> visit(element)
is CgDocPreTagStatement -> visit(element)
is CgCustomTagStatement -> visit(element)
is CgDocCodeStmt -> visit(element)
is CgDocRegularStmt -> visit(element)
is CgDocClassLinkStmt -> visit(element)
Expand Down Expand Up @@ -335,6 +337,11 @@ class CgDocPreTagStatement(content: List<CgDocStatement>) : CgDocTagStatement(co
override fun hashCode(): Int = content.hashCode()
}

/**
* Represents a type for statements containing custom JavaDoc tags.
*/
data class CgCustomTagStatement(val statements: List<CgDocStatement>) : CgDocTagStatement(statements)

class CgDocCodeStmt(val stmt: String) : CgDocStatement() {
override fun isEmpty(): Boolean = stmt.isEmpty()

Expand Down Expand Up @@ -379,6 +386,10 @@ fun convertDocToCg(stmt: DocStatement): CgDocStatement {
val stmts = stmt.content.map { convertDocToCg(it) }
CgDocPreTagStatement(content = stmts)
}
is DocCustomTagStatement -> {
val stmts = stmt.content.map { convertDocToCg(it) }
CgCustomTagStatement(statements = stmts)
}
is DocRegularStmt -> CgDocRegularStmt(stmt = stmt.stmt)
is DocClassLinkStmt -> CgDocClassLinkStmt(className = stmt.className)
is DocMethodLinkStmt -> CgDocMethodLinkStmt(methodName = stmt.methodName, stmt = stmt.className)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.utbot.framework.codegen.model.tree.CgComment
import org.utbot.framework.codegen.model.tree.CgCommentedAnnotation
import org.utbot.framework.codegen.model.tree.CgComparison
import org.utbot.framework.codegen.model.tree.CgContinueStatement
import org.utbot.framework.codegen.model.tree.CgCustomTagStatement
import org.utbot.framework.codegen.model.tree.CgDeclaration
import org.utbot.framework.codegen.model.tree.CgDecrement
import org.utbot.framework.codegen.model.tree.CgDoWhileLoop
Expand Down Expand Up @@ -309,11 +310,19 @@ internal abstract class CgAbstractRenderer(val context: CgContext, val printer:
}
override fun visit(element: CgDocPreTagStatement) {
if (element.content.all { it.isEmpty() }) return

println("<pre>")
for (stmt in element.content) stmt.accept(this)
println("</pre>")
}

override fun visit(element: CgCustomTagStatement) {
if (element.statements.all { it.isEmpty() }) return

for (stmt in element.statements) {
stmt.accept(this)
}
}

override fun visit(element: CgDocCodeStmt) {
if (element.isEmpty()) return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.utbot.framework.codegen.model.tree.CgDocClassLinkStmt
import org.utbot.framework.codegen.model.tree.CgDocCodeStmt
import org.utbot.framework.codegen.model.tree.CgDocMethodLinkStmt
import org.utbot.framework.codegen.model.tree.CgDocPreTagStatement
import org.utbot.framework.codegen.model.tree.CgCustomTagStatement
import org.utbot.framework.codegen.model.tree.CgDocRegularStmt
import org.utbot.framework.codegen.model.tree.CgDocumentationComment
import org.utbot.framework.codegen.model.tree.CgElement
Expand Down Expand Up @@ -122,6 +123,7 @@ interface CgVisitor<R> {

// Comment statements
fun visit(element: CgDocPreTagStatement): R
fun visit(element: CgCustomTagStatement): R
fun visit(element: CgDocCodeStmt): R
fun visit(element: CgDocRegularStmt): R
fun visit(element: CgDocClassLinkStmt): R
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.utbot.framework.coverage.toAtLeast
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.framework.plugin.api.DocClassLinkStmt
import org.utbot.framework.plugin.api.DocCodeStmt
import org.utbot.framework.plugin.api.DocCustomTagStatement
import org.utbot.framework.plugin.api.DocMethodLinkStmt
import org.utbot.framework.plugin.api.DocPreTagStatement
import org.utbot.framework.plugin.api.DocRegularStmt
Expand Down Expand Up @@ -2746,6 +2747,7 @@ private fun flattenDocStatements(summary: List<DocStatement>): List<DocStatement
is DocMethodLinkStmt -> flatten.add(s)
is DocCodeStmt -> flatten.add(s)
is DocRegularStmt -> flatten.add(s)
is DocCustomTagStatement -> flatten.add(s)
}
}
return flatten
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.utbot.intellij.plugin.javadoc

import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.javadoc.CustomJavadocTagProvider
import com.intellij.psi.javadoc.JavadocTagInfo
import com.intellij.psi.javadoc.PsiDocTagValue
import org.utbot.summary.comment.CustomJavaDocTag
import org.utbot.summary.comment.CustomJavaDocTagProvider

/**
* Provides plugin's custom JavaDoc tags to make test summaries structured.
*/
class UtCustomJavaDocTagProvider : CustomJavadocTagProvider {
override fun getSupportedTags(): List<UtCustomTagInfo> =
CustomJavaDocTagProvider().getPluginCustomTags().map { UtCustomTagInfo(it) }

class UtCustomTagInfo(private val tag: CustomJavaDocTag) : JavadocTagInfo {
override fun getName(): String = tag.name

fun getMessage(): String = tag.message

override fun isInline() = false

override fun checkTagValue(value: PsiDocTagValue?): String? = null

override fun getReference(value: PsiDocTagValue?): PsiReference? = null

override fun isValidInContext(element: PsiElement?): Boolean = element is PsiMethod
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.utbot.intellij.plugin.javadoc

import com.intellij.codeInsight.javadoc.JavaDocExternalFilter
import com.intellij.codeInsight.javadoc.JavaDocInfoGeneratorFactory
import com.intellij.lang.java.JavaDocumentationProvider
import com.intellij.psi.PsiDocCommentBase
import com.intellij.psi.PsiJavaDocumentedElement

/**
* To render UtBot custom JavaDoc tags messages, we need to override basic behaviour of [JavaDocumentationProvider].
* The IJ platform knows only custom tag names, so we need to add their messages in rendered comments to make it look nice.
*/
class UtDocumentationProvider : JavaDocumentationProvider() {
override fun generateRenderedDoc(comment: PsiDocCommentBase): String? {
val target = comment.owner ?: comment

if (target !is PsiJavaDocumentedElement) {
return ""
}

val baseJavaDocInfoGenerator = JavaDocInfoGeneratorFactory.getBuilder(target.getProject())
.setPsiElement(target)
.setIsGenerationForRenderedDoc(true)
.create()

val finalDocContent = replaceTagNamesWithMessages(baseJavaDocInfoGenerator.generateRenderedDocInfo())

return JavaDocExternalFilter.filterInternalDocInfo(finalDocContent)
}

/**
* Replaces names of plugin's custom JavaDoc tags with their messages in the comment generated by the IJ platform.
* Example: utbot.MethodUnderTest -> Method under test.
*/
private fun replaceTagNamesWithMessages(comment: String?) =
comment?.let {
val docTagProvider = UtCustomJavaDocTagProvider()
docTagProvider.supportedTags.fold(it) { result, tag ->
if (result.contains(tag.name)) {
result.replace(tag.name, "${tag.getMessage()}:")
} else {
result
}
}
} ?: ""
}
3 changes: 3 additions & 0 deletions utbot-intellij/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
<registryKey defaultValue="false" description="Enable editing Kotlin test files" key="kotlin.ultra.light.classes.empty.text.range"/>
<postStartupActivity implementation="org.utbot.intellij.plugin.ui.GotItTooltipActivity"/>
<projectModelModifier implementation="org.utbot.intellij.plugin.util.UtProjectModelModifier"/>
<!--Documentation-->
<customJavadocTagProvider implementation="org.utbot.intellij.plugin.javadoc.UtCustomJavaDocTagProvider"/>
<lang.documentationProvider language="JAVA" order="first" implementationClass="org.utbot.intellij.plugin.javadoc.UtDocumentationProvider"/>
</extensions>

<!-- Minimum and maximum build of IDE compatible with the plugin -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package examples

import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
import org.utbot.framework.UtSettings

/**
* Controls the value of useCustomJavaDocTags global variable.
*
* Should be used in summary tests containing custom JavaDoc tags.
* To use it, add an annotation @ExtendWith(CustomJavaDocTagsEnabler::class) under test class.
*/
class CustomJavaDocTagsEnabler(private val enable: Boolean = true) : BeforeEachCallback, AfterEachCallback {
private var previousValue = false

override fun beforeEach(context: ExtensionContext?) {
previousValue = UtSettings.useCustomJavaDocTags
UtSettings.useCustomJavaDocTags = enable
}

override fun afterEach(context: ExtensionContext?) {
UtSettings.useCustomJavaDocTags = previousValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package examples.controlflow

import examples.CustomJavaDocTagsEnabler
import examples.SummaryTestCaseGeneratorTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.utbot.examples.DoNotCalculate
import org.utbot.examples.controlflow.Conditions
import org.utbot.framework.plugin.api.MockStrategyApi

@ExtendWith(CustomJavaDocTagsEnabler::class)
class SummaryConditionsTest : SummaryTestCaseGeneratorTest(
Conditions::class
) {
@Test
fun testSimpleCondition() {
val summary1 = "@utbot.classUnderTest {@link Conditions}\n" +
"@utbot.methodUnderTest {@link org.utbot.examples.controlflow.Conditions#simpleCondition(boolean)}\n" +
"@utbot.executesCondition {@code (condition): False}\n" +
"@utbot.returnsFrom {@code return 0;}"

val summary2 = "@utbot.classUnderTest {@link Conditions}\n" +
"@utbot.methodUnderTest {@link org.utbot.examples.controlflow.Conditions#simpleCondition(boolean)}\n" +
"@utbot.executesCondition {@code (condition): True}\n" +
"@utbot.returnsFrom {@code return 1;}"

val methodName1 = "testSimpleCondition_NotCondition"
val methodName2 = "testSimpleCondition_Condition"

val displayName1 = "condition : False -> return 0"
val displayName2 = "condition : True -> return 1"

val summaryKeys = listOf(
summary1,
summary2
)

val displayNames = listOf(
displayName1,
displayName2
)

val methodNames = listOf(
methodName1,
methodName2
)

val method = Conditions::simpleCondition
val mockStrategy = MockStrategyApi.NO_MOCKS
val coverage = DoNotCalculate

summaryCheck(method, mockStrategy, coverage, summaryKeys, methodNames, displayNames)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package examples.exceptions

import examples.CustomJavaDocTagsEnabler
import examples.SummaryTestCaseGeneratorTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.utbot.examples.DoNotCalculate
import org.utbot.examples.exceptions.ExceptionClusteringExamples
import org.utbot.framework.plugin.api.MockStrategyApi

@ExtendWith(CustomJavaDocTagsEnabler::class)
class SummaryExceptionClusteringExamplesTest : SummaryTestCaseGeneratorTest(
ExceptionClusteringExamples::class
) {
@Test
fun testDifferentExceptions() {
val summary1 = "@utbot.classUnderTest {@link ExceptionClusteringExamples}\n" +
"@utbot.methodUnderTest {@link org.utbot.examples.exceptions.ExceptionClusteringExamples#differentExceptions(int)}\n" +
"@utbot.executesCondition {@code (i == 0): True}\n" +
"@utbot.throwsException {@link java.lang.ArithmeticException} in: return 100 / i;"

val summary2 = "@utbot.classUnderTest {@link ExceptionClusteringExamples}\n" +
"@utbot.methodUnderTest {@link org.utbot.examples.exceptions.ExceptionClusteringExamples#differentExceptions(int)}\n" +
"@utbot.executesCondition {@code (i == 0): False},\n" +
"{@code (i == 1): True}\n" +
"@utbot.throwsException {@link org.utbot.examples.exceptions.MyCheckedException} after condition: i == 1"
val summary3 = "@utbot.classUnderTest {@link ExceptionClusteringExamples}\n" +
"@utbot.methodUnderTest {@link org.utbot.examples.exceptions.ExceptionClusteringExamples#differentExceptions(int)}\n" +
"@utbot.executesCondition {@code (i == 0): False},\n" +
"{@code (i == 1): False},\n" +
"{@code (i == 2): True}\n" +
"@utbot.throwsException {@link java.lang.IllegalArgumentException} after condition: i == 2"
val summary4 = "@utbot.classUnderTest {@link ExceptionClusteringExamples}\n" +
"@utbot.methodUnderTest {@link org.utbot.examples.exceptions.ExceptionClusteringExamples#differentExceptions(int)}\n" +
"@utbot.executesCondition {@code (i == 0): False},\n" +
"{@code (i == 1): False},\n" +
"{@code (i == 2): False}\n" +
"@utbot.returnsFrom {@code return i * 2;}\n"

val methodName1 = "testDifferentExceptions_IEqualsZero"
val methodName2 = "testDifferentExceptions_IEquals1"
val methodName3 = "testDifferentExceptions_IEquals2"
val methodName4 = "testDifferentExceptions_INotEquals2"

val displayName1 = "return 100 / i : True -> ThrowArithmeticException"
val displayName2 = "i == 1 -> ThrowMyCheckedException"
val displayName3 = "i == 2 -> ThrowIllegalArgumentException"
val displayName4 = "i == 0 : False -> return i * 2"

val summaryKeys = listOf(
summary1,
summary2,
summary3,
summary4
)

val displayNames = listOf(
displayName1,
displayName2,
displayName3,
displayName4
)

val methodNames = listOf(
methodName1,
methodName2,
methodName3,
methodName4
)

val method = ExceptionClusteringExamples::differentExceptions
val mockStrategy = MockStrategyApi.NO_MOCKS
val coverage = DoNotCalculate

summaryCheck(method, mockStrategy, coverage, summaryKeys, methodNames, displayNames)
}
}
Loading