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

Feature/1066/attribute descriptor for sourcecodeparser #3166

Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/)

## [unreleased] (Added 🚀 | Changed | Removed 🗑 | Fixed 🐞 | Chore 👨‍💻 👩‍💻)

### Chore 👨‍💻 👩‍💻

- Add attribute descriptors to SoureCodeParser [#3166](https://github.com/MaibornWolff/codecharta/pull/3166)

## [1.112.1] - 2022-12-01

### Fixed 🐞
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fun getAttributeDescriptors(): Map<String, AttributeDescriptor> {
descriptors["classes"] = AttributeDescriptor(title = "Number of Classes", description = "Number of classes", link = ghLink)
descriptors["functions_per_class"] = AttributeDescriptor(title = "Functions per Class", description = "Number of functions per class", link = ghLink)
descriptors["average_statements_per_function"] = AttributeDescriptor(title = "Average Statements per Function", description = "Average number of statements per method", link = ghLink)
descriptors["max_function_mcc"] = AttributeDescriptor(title = "Maximum Cyclic Complexity", description = "Maximum cyclic complexity of a function", link = ghLink)
descriptors["max_function_mcc"] = AttributeDescriptor(title = "Function Complexity", description = "Maximum cyclic complexity based on paths through a function by McCabe", link = ghLink)
descriptors["max_block_depth"] = AttributeDescriptor(title = "Maximum Block Depth", description = "Maximum nested block depth found", link = ghLink)
descriptors["average_block_depth"] = AttributeDescriptor(title = "Average Block Depth", description = "Average nested block depth found", link = ghLink)
descriptors["average_function_mcc"] = AttributeDescriptor(title = "Average Cyclic Complexity", description = "Average cyclic complexity of functions", link = ghLink)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ fun getAttributeDescriptors(): Map<String, AttributeDescriptor> {
val descriptors = mutableMapOf<String, AttributeDescriptor>()
val npmLink = "https://www.npmjs.com/package/metric-gardener"

descriptors["mcc"] = AttributeDescriptor(title = "Maximum Cyclic Complexity", description = "Maximum cyclic complexity", link = npmLink)
descriptors["mcc"] = AttributeDescriptor(title = "Maximum Cyclic Complexity", description = "Maximum cyclic complexity based on paths through the code by McCabe", link = npmLink)
descriptors["functions"] = AttributeDescriptor(title = "Number of Functions", description = "Number of functions", link = npmLink)
descriptors["classes"] = AttributeDescriptor(title = "Number of Classes", description = "Number of classes", link = npmLink)
descriptors["lines_of_code"] = AttributeDescriptor(title = "Lines of Code", description = "Lines of code including empty lines and comments", link = npmLink)
descriptors["comment_lines"] = AttributeDescriptor(title = "Comment Lines", description = "Number of lines containing either comment or commented-out code", link = npmLink)
descriptors["comment_lines"] = AttributeDescriptor(title = "Comment Lines", description = "Number of lines containing either a comment or commented-out code", link = npmLink)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In metricTitle.js: "Number of Code Lines with Comments"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the current title, because it is shorter and still understandable.

descriptors["real_lines_of_code"] = AttributeDescriptor(title = "Real Lines of Code", description = "Number of physical lines that contain at least one character which is neither a whitespace nor a tabulation nor part of a comment", link = npmLink)

return descriptors.toMap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ fun getAttributeDescriptors(): Map<String, AttributeDescriptor> {
descriptors["blocker_violations"] = AttributeDescriptor(title = "Blocker Violations", description = "Total count of issues of the severity blocker", link = metricLink)
descriptors["branch_coverage"] = AttributeDescriptor(title = "Branch Coverage", description = "Density of fully covered boolean conditions in flow control structures", link = metricLink)
descriptors["bugs"] = AttributeDescriptor(title = "Number of Bugs", description = "Number of bug issues", link = metricLink)
descriptors["class_complexity"] = AttributeDescriptor(title = "Cyclic Class Complexity", description = "Cyclomatic complexity based on paths through a class", link = metricLink)
descriptors["class_complexity"] = AttributeDescriptor(title = "Cyclic Class Complexity", description = "Maximum cyclomatic complexity based on paths through a class by McCabe", link = metricLink)
descriptors["classes"] = AttributeDescriptor(title = "Number of Classes", description = "Number of classes (including nested classes, interfaces, enums and annotations", link = metricLink)
descriptors["code_smells"] = AttributeDescriptor(title = "Code Smells", description = "Total count of code smell issues", link = metricLink)
descriptors["cognitive_complexity"] = AttributeDescriptor(title = "Cognitive Complexity", description = "How hard is it to understand the code's control flow", link = "https://www.sonarsource.com/resources/cognitive-complexity/")
descriptors["comment_lines"] = AttributeDescriptor(title = "Comment Lines", description = "Number of lines containing either comment or commented-out code", link = metricLink)
descriptors["comment_lines"] = AttributeDescriptor(title = "Comment Lines", description = "Number of lines containing either a comment or commented-out code", link = metricLink)
descriptors["comment_lines_density"] = AttributeDescriptor(title = "Comment Density", description = "Density of comment lines in relation to total lines of code", link = metricLink)
descriptors["complexity"] = AttributeDescriptor(title = "Cyclomatic Complexity", description = "Cyclomatic complexity based on paths through the code", link = metricLink)
descriptors["complexity_in_classes"] = AttributeDescriptor(title = "Class Complexity", description = "Cyclomatic complexity based on paths through a class", link = metricLink)
descriptors["complexity_in_functions"] = AttributeDescriptor(title = "Function Complexity", description = "Cyclomatic complexity based on paths through a function", link = metricLink)
descriptors["complexity"] = AttributeDescriptor(title = "Maximum Cyclomatic Complexity", description = "Maximum cyclic complexity based on paths through the code by McCabe", link = metricLink)
descriptors["complexity_in_classes"] = AttributeDescriptor(title = "Class Complexity", description = "Maximum cyclomatic complexity based on paths through a class by McCabe", link = metricLink)
descriptors["complexity_in_functions"] = AttributeDescriptor(title = "Function Complexity", description = "Maximum cyclomatic complexity based on paths through a function by McCabe", link = metricLink)
descriptors["conditions_to_cover"] = AttributeDescriptor(title = "Conditions to Cover", description = "Number of conditions which could be covered by unit tests", link = metricLink)
descriptors["confirmed_issues"] = AttributeDescriptor(title = "Confirmed Issues", description = "Total count of issues in the confirmed state", link = metricLink)
descriptors["coverage"] = AttributeDescriptor(title = "Coverage", description = "Mix of branch and line coverage", link = metricLink)
Expand All @@ -27,9 +27,9 @@ fun getAttributeDescriptors(): Map<String, AttributeDescriptor> {
descriptors["duplicated_lines"] = AttributeDescriptor(title = "Duplicated Lines", description = "Number of lines involved in duplications", link = metricLink)
descriptors["duplicated_lines_density"] = AttributeDescriptor(title = "Duplicated Line Density", description = "Density of duplicated lines", link = metricLink)
descriptors["false_positive_issues"] = AttributeDescriptor(title = "False Positive Issues", description = "Total count of issues marked false positive", link = metricLink)
descriptors["file_complexity"] = AttributeDescriptor(title = "File Complexity", description = "Cyclomatic complexity based on paths through a file", link = metricLink)
descriptors["file_complexity"] = AttributeDescriptor(title = "File Complexity", description = "Maximum cyclomatic complexity based on paths through a file by McCabe", link = metricLink)
descriptors["files"] = AttributeDescriptor(title = "Number of Files", description = "Number of files", link = metricLink)
descriptors["function_complexity"] = AttributeDescriptor(title = "Function Complexity", description = "Cyclomatic complexity based on paths through a function", link = metricLink)
descriptors["function_complexity"] = AttributeDescriptor(title = "Function Complexity", description = "Maximum cyclomatic complexity based on paths through a function by McCabe", link = metricLink)
descriptors["functions"] = AttributeDescriptor(title = "Number of Functions", description = "Number of functions", link = metricLink)
descriptors["generated_lines"] = AttributeDescriptor(title = "Generated Lines", description = "Number of generated lines of code", link = metricLink)
descriptors["generated_ncloc"] = AttributeDescriptor(title = "Generated Real Lines of Code", description = "Number of generated non-empty lines of code", link = metricLink)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.maibornwolff.codecharta.importer.sourcecodeparser

import de.maibornwolff.codecharta.model.AttributeDescriptor

fun getAttributeDescriptors(): Map<String, AttributeDescriptor> {
val descriptors = mutableMapOf<String, AttributeDescriptor>()
val ghLink = "https://github.com/MaibornWolff/codecharta/blob/main/analysis/import/SourceCodeParser/README.md"
descriptors["rloc"] = AttributeDescriptor(title = "Real Lines of Code", description = "Number of physical lines that contain at least one character which is neither a whitespace nor a tabulation nor part of a comment", link = ghLink)
descriptors["classes"] = AttributeDescriptor(title = "Number of Classes", description = "Number of classes (including nested classes, interfaces, enums and annotations", link = ghLink)
descriptors["functions"] = AttributeDescriptor(title = "Number of Functions", description = "Number of functions", link = ghLink)
descriptors["statements"] = AttributeDescriptor(title = "Number of Statements", description = "Number of statements", link = ghLink)
descriptors["comment_lines"] = AttributeDescriptor(title = "Comment Lines", description = "Number of lines containing either a comment or commented-out code", link = ghLink)
descriptors["mcc"] = AttributeDescriptor(title = "Maximum Cyclic Complexity", description = "Maximum cyclic complexity based on paths through the code by McCabe", link = ghLink)
descriptors["cognitive_complexity"] = AttributeDescriptor(title = "Cognitive Complexity", description = "How hard is it to understand the code's control flow", link = "https://www.sonarsource.com/resources/cognitive-complexity/")
descriptors["commented_out_code_blocks"] = AttributeDescriptor(title = "Commented-out Code Blocks", description = "Number of blocks of commented-out code", link = ghLink)
descriptors["max_nesting_level"] = AttributeDescriptor(title = "Maximum Nesting Level", description = "Maximum nested level/depth found", link = ghLink)
descriptors["code_smell"] = AttributeDescriptor(title = "Code Smells", description = "Total count of code smell issues", link = ghLink)
descriptors["security_hotspot"] = AttributeDescriptor(title = "Security Hotspots", description = "Number of security hotspots", link = ghLink)
descriptors["vulnerability"] = AttributeDescriptor(title = "Number of Vulnerabilities", description = "Number of vulnerability issues", link = ghLink)
descriptors["bug"] = AttributeDescriptor(title = "Number of Bugs", description = "Number of bug issues", link = ghLink)
descriptors["sonar_issue_other"] = AttributeDescriptor(title = "Other Sonar Issues", description = "Total number of other sonar issues", link = ghLink)

return descriptors.toMap()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.maibornwolff.codecharta.importer.sourcecodeparser

import picocli.CommandLine
import java.util.Locale

enum class OutputFormat {
JSON, TABLE
Expand All @@ -11,7 +12,7 @@ class OutputTypeConverter : CommandLine.ITypeConverter<OutputFormat> {
OutputFormat.TABLE.name.equals(value, ignoreCase = true) -> OutputFormat.TABLE
OutputFormat.JSON.name.equals(value, ignoreCase = true) -> OutputFormat.JSON
else -> {
System.err.println("Using default ${OutputFormat.JSON.name.toLowerCase()}"); OutputFormat.JSON
System.err.println("Using default ${OutputFormat.JSON.name.lowercase(Locale.getDefault())}"); OutputFormat.JSON
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,6 @@ package de.maibornwolff.codecharta.importer.sourcecodeparser.metrics
class ProjectMetrics {
val projectMetrics = mutableMapOf<String, FileMetricMap>()

// TODO remove because it is only used in tests
fun addFile(file: String): ProjectMetrics {
projectMetrics[file] = FileMetricMap()
return this
}

// TODO remove because it is only used in tests
fun addMetricToFile(file: String, metric: String, value: Number) {
projectMetrics[file]?.add(metric, value)
}

fun addFileMetricMap(file: String, fileMetricMap: FileMetricMap) {
projectMetrics[file] = fileMetricMap
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.maibornwolff.codecharta.importer.sourcecodeparser.metricwriters

import de.maibornwolff.codecharta.filter.mergefilter.MergeFilter
import de.maibornwolff.codecharta.importer.sourcecodeparser.getAttributeDescriptors
import de.maibornwolff.codecharta.importer.sourcecodeparser.metrics.FileMetricMap
import de.maibornwolff.codecharta.importer.sourcecodeparser.metrics.ProjectMetrics
import de.maibornwolff.codecharta.model.MutableNode
Expand All @@ -16,7 +17,7 @@ class JSONMetricWriter(private val outputStream: OutputStream, private val toCom
override fun generate(projectMetrics: ProjectMetrics, allMetrics: Set<String>, pipedProject: Project?) {
projectMetrics.projectMetrics.forEach { addAsNode(it) }

var project = projectBuilder.build()
var project = projectBuilder.addAttributeDescriptions(getAttributeDescriptors()).build(true)
if (pipedProject != null) {
project = MergeFilter.mergePipedWithCurrentProject(pipedProject, project)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import java.nio.charset.StandardCharsets
import java.util.Locale
import kotlin.collections.HashMap

class JavaSonarAnalyzer(verbose: Boolean = false, searchIssues: Boolean = true) : SonarAnalyzer(verbose, searchIssues) {

Expand Down Expand Up @@ -188,7 +190,7 @@ class JavaSonarAnalyzer(verbose: Boolean = false, searchIssues: Boolean = true)

sensorContext.allIssues().forEach {
val ruleKey = it.ruleKey().rule()
val type = issueRepository.rule(ruleKey)?.type().toString().toLowerCase()
val type = issueRepository.rule(ruleKey)?.type().toString().lowercase(Locale.getDefault())
if (verbose) System.err.println("Found: $type ${it.ruleKey().rule()} \n with message ${it.primaryLocation().message()}")
if (issues.containsKey(type)) {
issues[type] = issues[type]!! + 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.maibornwolff.codecharta.importer.sourcecodeparser
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import de.maibornwolff.codecharta.importer.sourcecodeparser.metrics.FileMetricMap
import de.maibornwolff.codecharta.importer.sourcecodeparser.metrics.ProjectMetrics
import de.maibornwolff.codecharta.importer.sourcecodeparser.sonaranalyzers.SonarAnalyzer
import org.assertj.core.api.Assertions.assertThat
Expand All @@ -28,17 +29,20 @@ class ProjectParserTest {
}

private fun createmMockResponseJava(): ProjectMetrics {
val result = ProjectMetrics().addFile("foo.java").addFile("bar/foo.java")
result.addMetricToFile("foo.java", "functions", 4)
result.addMetricToFile("bar/foo.java", "functions", 7)
result.addMetricToFile("foo.java", "ncloc", 31)
result.addMetricToFile("bar/foo.java", "ncloc", 44)
val result = ProjectMetrics()
addFileInProject(result, "foo.java")
addFileInProject(result, "bar/foo.java")
addMetricToFileInProject(result, "foo.java", "functions", 4)
addMetricToFileInProject(result, "bar/foo.java", "functions", 7)
addMetricToFileInProject(result, "foo.java", "ncloc", 31)
addMetricToFileInProject(result, "bar/foo.java", "ncloc", 44)
return result
}

private fun createmMockResponsePython(): ProjectMetrics {
val result = ProjectMetrics().addFile("foo.py")
result.addMetricToFile("foo.py", "something_else", 42)
val result = ProjectMetrics()
addFileInProject(result, "foo.py")
addMetricToFileInProject(result, "foo.py", "something_else", 42)
return result
}

Expand Down Expand Up @@ -112,4 +116,12 @@ class ProjectParserTest {

assertThat(projectParser.metricKinds.toString()).contains("functions", "ncloc", "something_else")
}

private fun addFileInProject(currentProject: ProjectMetrics, file: String) {
currentProject.projectMetrics[file] = FileMetricMap()
}

private fun addMetricToFileInProject(currentProject: ProjectMetrics, file: String, metric: String, value: Int) {
currentProject.projectMetrics[file]?.add(metric, value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ class ProjectMetricsTest {

@Test
fun `addFile adds a file with empty Metric Map`() {
projectMetrics.addFile("foo")
addFileInProject(projectMetrics, "foo")

Assertions.assertThat(projectMetrics.projectMetrics["foo"]).isEqualToComparingFieldByField(FileMetricMap())
}

@Test
fun `AddMetricToFile adds correct metric to given file`() {
projectMetrics.addFile("foo")
addFileInProject(projectMetrics, "foo")
val expected = FileMetricMap().add("mcc", 99)

projectMetrics.addMetricToFile("foo", "mcc", 99)
addMetricToFileInProject(projectMetrics, "foo", "mcc", 99)

Assertions.assertThat(projectMetrics.projectMetrics["foo"]).isEqualToComparingFieldByField(expected)
}
Expand Down Expand Up @@ -64,4 +64,12 @@ class ProjectMetricsTest {

Assertions.assertThat(listOf("foo", "bar")).contains(result)
}

private fun addFileInProject(currentProject: ProjectMetrics, file: String) {
currentProject.projectMetrics[file] = FileMetricMap()
}

private fun addMetricToFileInProject(currentProject: ProjectMetrics, file: String, metric: String, value: Int) {
currentProject.projectMetrics[file]?.add(metric, value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class CSVMetricWriterTest {
@Test
fun `paths for files are correct`() {
val metrics = ProjectMetrics()
metrics.addFile("myFile.java")
metrics.addFile("foo/bar/myFile.java")
addFileInProject(metrics, "myFile.java")
addFileInProject(metrics, "foo/bar/myFile.java")
val result = ByteArrayOutputStream()

CSVMetricWriter(OutputStreamWriter(PrintStream(result))).generate(metrics, setOf())
Expand Down Expand Up @@ -74,4 +74,8 @@ class CSVMetricWriterTest {

assertThat(result.toString()).isEqualTo("file,foo,bar\nbla,,\n")
}

private fun addFileInProject(currentProject: ProjectMetrics, file: String) {
currentProject.projectMetrics[file] = FileMetricMap()
}
}