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

Documentation on symbols in test classes #271

Merged
merged 6 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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 @@ -11,7 +11,6 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import java.io.BufferedReader
import java.io.File
import java.util.stream.Collectors
import org.cqfn.diktat.common.config.reader.JsonResourceConfigReader
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cqfn.diktat.ruleset.rules.kdoc

import com.pinterest.ktlint.core.KtLint.FILE_PATH_USER_DATA_KEY
import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
Expand All @@ -8,15 +9,13 @@ import com.pinterest.ktlint.core.ast.ElementType.FUN
import com.pinterest.ktlint.core.ast.ElementType.KDOC
import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST
import com.pinterest.ktlint.core.ast.ElementType.PROPERTY
import org.cqfn.diktat.common.config.rules.RuleConfiguration
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getCommonConfig
import org.cqfn.diktat.ruleset.constants.Warnings
import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_CLASS_ELEMENTS
import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.getIdentifierName
import org.cqfn.diktat.ruleset.utils.isAccessibleOutside
import org.cqfn.diktat.ruleset.utils.isStandardMethod
import org.cqfn.diktat.ruleset.utils.*
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet

Expand All @@ -41,10 +40,13 @@ class KdocComments(private val configRules: List<RulesConfig>) : Rule("kdoc-comm
emitWarn = emit
isFixMode = autoCorrect

when (node.elementType) {
FILE -> checkTopLevelDoc(node)
CLASS -> checkClassElements(node)
}
val config = KdocCommentsConfiguration(configRules.getCommonConfig()?.configuration ?: mapOf())
val fileName = node.getRootNode().getUserData(FILE_PATH_USER_DATA_KEY)!!
if (!(node.hasTestAnnotation() || isLocatedInTest(fileName.splitPathToDirs(), config.testAnchors)))
when (node.elementType) {
FILE -> checkTopLevelDoc(node)
CLASS -> checkClassElements(node)
}
}

private fun checkClassElements(node: ASTNode) {
Expand Down Expand Up @@ -79,3 +81,10 @@ class KdocComments(private val configRules: List<RulesConfig>) : Rule("kdoc-comm
}
}
}

private class KdocCommentsConfiguration(config: Map<String, String>) : RuleConfiguration(config) {
/**
* Names of directories which indicate that this is path to tests. Will be checked like "src/$testAnchor" for each entry.
*/
val testAnchors = config.getOrDefault("testDirs", "test").split(',')
kentr0w marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.pinterest.ktlint.core.ast.ElementType.TYPE_REFERENCE
import com.pinterest.ktlint.core.ast.ElementType.WHEN_CONDITION_WITH_EXPRESSION
import org.cqfn.diktat.common.config.rules.RuleConfiguration
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.common.config.rules.getCommonConfig
import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTION
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_TRIVIAL_KDOC_ON_FUNCTION
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_WITHOUT_PARAM_TAG
Expand Down Expand Up @@ -62,10 +62,9 @@ class KdocMethods(private val configRules: List<RulesConfig>) : Rule("kdoc-metho
emitWarn = emit

if (node.elementType == FUN && node.getFirstChildWithType(MODIFIER_LIST).isAccessibleOutside()) {
val config = KdocMethodsConfiguration(configRules.getRuleConfig(MISSING_KDOC_ON_FUNCTION)?.configuration
?: mapOf())
val config = KdocMethodsConfiguration(configRules.getCommonConfig()?.configuration ?: mapOf())
val fileName = node.getRootNode().getUserData(FILE_PATH_USER_DATA_KEY)!!
val isTestMethod = node.hasTestAnnotation() || node.isLocatedInTest(fileName.splitPathToDirs(), config.testAnchors)
val isTestMethod = node.hasTestAnnotation() || isLocatedInTest(fileName.splitPathToDirs(), config.testAnchors)
if (!isTestMethod && !node.isStandardMethod() && !node.isSingleLineGetterOrSetter()) {
checkSignatureDescription(node)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.isLeaf
import com.pinterest.ktlint.core.ast.isRoot
import com.pinterest.ktlint.core.ast.parent
import org.cqfn.diktat.ruleset.rules.PackageNaming
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.TokenType
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement
Expand Down Expand Up @@ -424,6 +425,32 @@ fun ASTNode.extractLineOfText(): String {
return text.joinToString(separator = "").trim()
}

/**
* Checks node has `@Test` annotation
*/
fun ASTNode.hasTestAnnotation(): Boolean {
return findChildByType(MODIFIER_LIST)
?.getAllChildrenWithType(ElementType.ANNOTATION_ENTRY)
?.flatMap { it.findAllNodesWithSpecificType(ElementType.CONSTRUCTOR_CALLEE) }
?.any { it.findLeafWithSpecificType(ElementType.IDENTIFIER)?.text == "Test" }
?: false
}

/**
* Checks node is located in file src/test/**/*Test.kt
* @param testAnchors names of test directories, e.g. "test", "jvmTest"
*/
fun isLocatedInTest(filePathParts: List<String>, testAnchors: List<String>): Boolean {
return filePathParts
.takeIf { it.contains(PackageNaming.PACKAGE_PATH_ANCHOR) }
?.run { subList(lastIndexOf(PackageNaming.PACKAGE_PATH_ANCHOR), size) }
?.run {
// e.g. src/test/ClassTest.kt, other files like src/test/Utils.kt are still checked
testAnchors.any { contains(it) } && last().substringBeforeLast('.').endsWith("Test")
}
?: false
}

data class ReplacementResult(val oldNodes: List<ASTNode>, val newNodes: List<ASTNode>) {
init {
require(oldNodes.size == newNodes.size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,6 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtParameterList

/**
* Checks whether function from this [ElementType.FUN] node has `@Test` annotation
*/
fun ASTNode.hasTestAnnotation(): Boolean {
checkNodeIsFun(this)
return findChildByType(MODIFIER_LIST)
?.getAllChildrenWithType(ANNOTATION_ENTRY)
?.flatMap { it.findAllNodesWithSpecificType(CONSTRUCTOR_CALLEE) }
?.any { it.findLeafWithSpecificType(IDENTIFIER)?.text == "Test" }
?: false
}

/**
* Checks whether function from this [ElementType.FUN] node is located in file src/test/**/*Test.kt
* @param testAnchors names of test directories, e.g. "test", "jvmTest"
*/
fun ASTNode.isLocatedInTest(filePathParts: List<String>, testAnchors: List<String>): Boolean {
checkNodeIsFun(this)
return filePathParts
.takeIf { it.contains(PACKAGE_PATH_ANCHOR) }
?.run { subList(lastIndexOf(PACKAGE_PATH_ANCHOR), size) }
?.run {
// e.g. src/test/ClassTest.kt, other files like src/test/Utils.kt are still checked
testAnchors.any { contains(it) } && last().substringBeforeLast('.').endsWith("Test")
}
?: false
}

fun ASTNode.hasParameters(): Boolean {
checkNodeIsFun(this)
val argList = this.argList()
Expand Down
1 change: 1 addition & 0 deletions diktat-rules/src/main/resources/diktat-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
- name: DIKTAT_COMMON
configuration:
domainName: your.package.name.here
testDirs: test
- name: CLASS_NAME_INCORRECT
enabled: true
configuration: {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.cqfn.diktat.ruleset.chapter2
import com.pinterest.ktlint.core.LintError
import org.cqfn.diktat.common.config.rules.RulesConfig
import generated.WarningNames
import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_TRIVIAL_KDOC_ON_FUNCTION
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_WITHOUT_PARAM_TAG
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_WITHOUT_RETURN_TAG
Expand Down Expand Up @@ -76,7 +77,7 @@ class KdocMethodsTest : LintTestBase(::KdocMethods) {
)
// should allow to set custom test dirs
lintMethod(funCode, fileName = "src/jvmTest/kotlin/org/cqfn/diktat/ExampleTest.kt",
rulesConfigList = listOf(RulesConfig(MISSING_KDOC_ON_FUNCTION.name, true, mapOf("testDirs" to "test,jvmTest")))
rulesConfigList = listOf(RulesConfig(DIKTAT_COMMON, true, mapOf("testDirs" to "test,jvmTest")))
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,33 @@ class KdocWarnTest : LintTestBase(::KdocComments) {
)
}

@Test
@Tag(WarningNames.MISSING_KDOC_CLASS_ELEMENTS)
fun `Kdoc shouldn't present for each class element because Test annotation`() {
lintMethod(
"""
/**
* class that contains fields, functions and public subclasses
**/
@Test
class SomeGoodName {
val variable: String = ""
private val privateVariable: String = ""
fun perfectFunction() {
}

private fun privateFunction() {
}

class InternalClass {
}

private class InternalClass {
}
}
""".trimIndent())
}

@Test
@Tag(WarningNames.MISSING_KDOC_CLASS_ELEMENTS)
fun `Kdoc should present for each class element (positive)`() {
Expand Down