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

Fix/3406/fix rawtextparser input handling 1 #3415

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/)
### Fixed 🐞

- Fix RawTextParser producing incorrect output when no (or multiple) file extensions were specified in interactive mode [#3405](https://github.com/MaibornWolff/codecharta/pull/3405)
- Fix handling of empty inputs for the `--metrics`, `--exclude`, `--file-extensions` flags in the RawTextParser [#3415](https://github.com/MaibornWolff/codecharta/pull/3415)

## [1.120.1] - 2023-11-17

Expand Down
1 change: 1 addition & 0 deletions analysis/model/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dependencies {
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
implementation group: 'io.github.microutils', name: 'kotlin-logging-jvm', version: '2.1.23'

implementation group: 'info.picocli', name: 'picocli', version: picocli_version
implementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4j_version

testImplementation group: 'org.jetbrains.kotlin', name: 'kotlin-test', version: kotlin_version
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.maibornwolff.codecharta.util

import picocli.CommandLine

class CommaSeparatedStringToListConverter : CommandLine.ITypeConverter<List<String>> {
override fun convert(value: String?): List<String> {
if (value != null) {
return value.split(",")
.map { e -> e.trim() }
.filter { e -> e.isNotEmpty() }
}
return listOf()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package de.maibornwolff.codecharta.util

import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test

class StringToListInputConverterTest {

@Test
fun `should return empty list for null input`() {
val input = null

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf<String>()

Assertions.assertThat(result).isEqualTo(expected)
}

@Test
fun `should return empty list for empty string input`() {
val input = ""

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf<String>()

Assertions.assertThat(result).isEqualTo(expected)
}

@Test
fun `should return list with single entry for single string input`() {
val input = "entry"

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf(input)

Assertions.assertThat(result).isEqualTo(expected)
}

@Test
fun `should return list with all entries for input string with multiple values`() {
val input = "entry1, entry2, entry3"

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf("entry1", "entry2", "entry3")

Assertions.assertThat(result).isEqualTo(expected)
}

@Test
fun `should remove whitespace for all entries`() {
val input = " entry1 , entry2, entry3, \n, \t, \r"

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf("entry1", "entry2", "entry3")

Assertions.assertThat(result).isEqualTo(expected)
}

@Test
fun `should return all but empty entries for input string with multiple values`() {
val input = "entry1, , entry2, , ,entry3"

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf("entry1", "entry2", "entry3")

Assertions.assertThat(result).isEqualTo(expected)
}

@Test
fun `should return empty list for input string with empty values only`() {
val input = " , , , ,,"

val stringToListInputConverter = CommaSeparatedStringToListConverter()
val result = stringToListInputConverter.convert(input)
val expected = listOf<String>()

Assertions.assertThat(result).isEqualTo(expected)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import java.util.concurrent.ConcurrentHashMap

class MetricCollector(
private var root: File,
private val exclude: Array<String> = arrayOf(),
private val fileExtensions: Array<String> = arrayOf(),
private val exclude: List<String> = listOf(),
private val fileExtensions: List<String> = listOf(),
private val parameters: Map<String, Int> = mapOf(),
private val metrics: List<String> = listOf()
) {
Expand Down Expand Up @@ -59,7 +59,6 @@ class MetricCollector(

private fun isParsableFileExtension(path: String): Boolean {
return fileExtensions.isEmpty() ||
fileExtensions.contentEquals(arrayOf("")) ||
fileExtensions.contains(path.substringAfterLast(".")) ||
fileExtensions.contains(".${path.substringAfterLast(".")}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import de.maibornwolff.codecharta.serialization.ProjectSerializer
import de.maibornwolff.codecharta.tools.interactiveparser.InteractiveParser
import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface
import de.maibornwolff.codecharta.tools.interactiveparser.util.CodeChartaConstants
import de.maibornwolff.codecharta.util.CommaSeparatedStringToListConverter
import de.maibornwolff.codecharta.util.InputHelper
import mu.KotlinLogging
import picocli.CommandLine
Expand Down Expand Up @@ -44,7 +45,8 @@ class RawTextParser(
@CommandLine.Option(
arity = "0..",
names = ["-m", "--metrics"],
description = ["metrics to be computed (select all if not specified)"]
description = ["metrics to be computed (select all if not specified)"],
converter = [(CommaSeparatedStringToListConverter::class)]
)
private var metrics: List<String> = listOf()

Expand All @@ -60,15 +62,18 @@ class RawTextParser(
@CommandLine.Option(names = ["--max-indentation-level"], description = ["maximum Indentation Level (default 10)"])
private var maxIndentLvl: Int? = null

@CommandLine.Option(names = ["-e", "--exclude"], description = ["exclude file/folder according to regex pattern"])
private var exclude: Array<String> = arrayOf()
@CommandLine.Option(
names = ["-e", "--exclude"],
description = ["exclude file/folder according to regex pattern"],
converter = [(CommaSeparatedStringToListConverter::class)])
private var exclude: List<String> = listOf()

@CommandLine.Option(
names = ["-fe", "--file-extensions"],
description = ["parse only files with specified extensions (default: any)"],
split = "\\s*,\\s*"
converter = [(CommaSeparatedStringToListConverter::class)]
)
private var fileExtensions: Array<String> = arrayOf()
private var fileExtensions: List<String> = listOf()

@CommandLine.Option(
names = ["--without-default-excludes"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MetricCollectorTest {

@Test
fun `Should exclude regex patterns`() {
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, exclude = arrayOf(".*\\.excluded$", "foobar")).parse()
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, exclude = listOf(".*\\.excluded$", "foobar")).parse()

Assertions.assertThat(result.size).isEqualTo(4)
Assertions.assertThat(result).containsKey("/spaces/spaces_3.included")
Expand All @@ -43,8 +43,8 @@ class MetricCollectorTest {
}

@Test
fun `Should include only specified File extension with one given`() {
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = arrayOf("includedtoo")).parse()
fun `Should include only specified File extensions`() {
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = listOf("includedtoo")).parse()

Assertions.assertThat(result.size).isEqualTo(1)
Assertions.assertThat(result).containsKey("/spaces/spaces_5.includedtoo")
Expand All @@ -54,7 +54,7 @@ class MetricCollectorTest {

@Test
fun `Should include only specified File extensions with multiple given`() {
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = arrayOf("included", "includedtoo")).parse()
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = listOf("included", "includedtoo")).parse()

Assertions.assertThat(result.size).isEqualTo(4)
Assertions.assertThat(result).containsKey("/spaces/spaces_3.included")
Expand All @@ -63,28 +63,16 @@ class MetricCollectorTest {
Assertions.assertThat(result).doesNotContainKey("/spaces/spaces_x_not_included.excluded")
}

@Test
fun `Should include all Files when no specific extensions were given (default for interactive mode)`() {
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = arrayOf("")).parse()

Assertions.assertThat(result.size).isEqualTo(5)
Assertions.assertThat(result).containsKey("/spaces/spaces_x_not_included.excluded")
Assertions.assertThat(result).containsKey("/spaces/spaces_3.included")
Assertions.assertThat(result).containsKey("/spaces/spaces_4.included")
Assertions.assertThat(result).containsKey("/spaces/spaces_5.includedtoo")
Assertions.assertThat(result).containsKey("/tabs.included")
}

@Test
fun `Should produce empty result if no valid file extensions were given`() {
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = arrayOf("none")).parse()
val result = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = listOf("none")).parse()
Assertions.assertThat(result.size).isEqualTo(0)
}

@Test
fun `Should produce the same result whether the user included a dot in the filetype or not`() {
val resultWithoutDot = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = arrayOf("included", "includedtoo")).parse()
val resultWithDot = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = arrayOf(".included", ".includedtoo")).parse()
val resultWithoutDot = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = listOf("included", "includedtoo")).parse()
val resultWithDot = MetricCollector(File("src/test/resources/sampleproject").absoluteFile, fileExtensions = listOf(".included", ".includedtoo")).parse()

Assertions.assertThat(resultWithoutDot == resultWithDot)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ class ParserDialogTest {
Assertions.assertThat(parseResult.matchedOption("metrics").getValue<List<String>>()).isEqualTo(listOf(metrics))
Assertions.assertThat(parseResult.matchedOption("max-indentation-level").getValue<Int>()).isEqualTo(maxIndentLvl.toInt())
Assertions.assertThat(parseResult.matchedOption("tab-width").getValue<Int>()).isEqualTo(tabWidthValue)
Assertions.assertThat(parseResult.matchedOption("file-extensions").getValue<Array<String>>()).isEqualTo(arrayOf(fileExtensions))
Assertions.assertThat(parseResult.matchedOption("file-extensions").getValue<List<String>>()).isEqualTo(listOf<String>())
Assertions.assertThat(parseResult.matchedOption("without-default-excludes").getValue<Boolean>()).isEqualTo(withoutDefaultExcludes)
Assertions.assertThat(parseResult.matchedOption("verbose").getValue<Boolean>()).isEqualTo(withoutDefaultExcludes)
Assertions.assertThat(parseResult.matchedOption("exclude").getValue<Array<String>>()).isEqualTo(arrayOf(exclude))
Assertions.assertThat(parseResult.matchedOption("exclude").getValue<List<String>>()).isEqualTo(listOf(exclude))
Assertions.assertThat(parseResult.matchedPositional(0).getValue<File>().name).isEqualTo(fileName)
}

Expand Down Expand Up @@ -119,10 +119,10 @@ class ParserDialogTest {
Assertions.assertThat(parseResult.matchedOption("metrics").getValue<List<String>>()).isEqualTo(listOf(metrics))
Assertions.assertThat(parseResult.matchedOption("max-indentation-level").getValue<Int>()).isEqualTo(maxIndentLvl.toInt())
Assertions.assertThat(parseResult.matchedOption("tab-width").getValue<Int>()).isEqualTo(tabWidthValue)
Assertions.assertThat(parseResult.matchedOption("file-extensions").getValue<Array<String>>()).isEqualTo(arrayOf(fileExtensions))
Assertions.assertThat(parseResult.matchedOption("file-extensions").getValue<List<String>>()).isEqualTo(listOf<String>())
Assertions.assertThat(parseResult.matchedOption("without-default-excludes").getValue<Boolean>()).isEqualTo(withoutDefaultExcludes)
Assertions.assertThat(parseResult.matchedOption("verbose").getValue<Boolean>()).isEqualTo(withoutDefaultExcludes)
Assertions.assertThat(parseResult.matchedOption("exclude").getValue<Array<String>>()).isEqualTo(arrayOf(exclude))
Assertions.assertThat(parseResult.matchedOption("exclude").getValue<List<String>>()).isEqualTo(listOf(exclude))
Assertions.assertThat(parseResult.matchedPositional(0).getValue<File>().name).isEqualTo(fileName)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,31 @@ class RawTextParserTest {
Assertions.assertThat(result).isEmpty()
Assertions.assertThat(messagesLogged).contains("No files with specified file extension(s) were found within the given folder - not generating an output file!")
}

@Test
fun `Should include all metrics when metrics-flag is empty string (default in interactive mode)`() {
val expectedResultFile = File("src/test/resources/cc_projects/project_3.cc.json").absoluteFile

val result = executeForOutput("", arrayOf("src/test/resources/sampleproject/tabs.included", "--metrics="))

JSONAssert.assertEquals(result, expectedResultFile.readText(), JSONCompareMode.NON_EXTENSIBLE)
}

@Test
fun `Should include all files when exclude-regex-flag is empty string (default in interactive mode)`() {
val expectedResultFile = File("src/test/resources/cc_projects/project_5.cc.json").absoluteFile

val result = executeForOutput("", arrayOf("src/test/resources/sampleproject/", "--exclude="))

JSONAssert.assertEquals(result, expectedResultFile.readText(), JSONCompareMode.NON_EXTENSIBLE)
}

@Test
fun `Should include all files when file-extensions-flag is empty string (default in interactive mode)`() {
val expectedResultFile = File("src/test/resources/cc_projects/project_5.cc.json").absoluteFile

val result = executeForOutput("", arrayOf("src/test/resources/sampleproject/", "--file-extensions="))

JSONAssert.assertEquals(result, expectedResultFile.readText(), JSONCompareMode.NON_EXTENSIBLE)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"checksum": "4e44036af555cc2dbb77bf7973e387c6",
"data": {
"projectName": "",
"nodes": [
{
"name": "root",
"type": "Folder",
"attributes": {},
"link": "",
"children": [
{
"name": "spaces",
"type": "Folder",
"attributes": {},
"link": "",
"children": [
{
"name": "spaces_3.included",
"type": "File",
"attributes": {
"indentation_level_0+": 5.0,
"indentation_level_1+": 3.0,
"indentation_level_2+": 2.0,
"indentation_level_3+": 0.0,
"indentation_level_4+": 0.0,
"indentation_level_5+": 0.0,
"indentation_level_6+": 0.0
},
"link": "",
"children": []
},
{
"name": "spaces_4.included",
"type": "File",
"attributes": {
"indentation_level_0+": 7.0,
"indentation_level_1+": 4.0,
"indentation_level_2+": 2.0,
"indentation_level_3+": 1.0,
"indentation_level_4+": 1.0,
"indentation_level_5+": 0.0,
"indentation_level_6+": 0.0
},
"link": "",
"children": []
},
{
"name": "spaces_5.includedtoo",
"type": "File",
"attributes": {
"indentation_level_0+": 5.0,
"indentation_level_1+": 3.0,
"indentation_level_2+": 1.0,
"indentation_level_3+": 0.0,
"indentation_level_4+": 0.0,
"indentation_level_5+": 0.0,
"indentation_level_6+": 0.0
},
"link": "",
"children": []
},
{
"name": "spaces_x_not_included.excluded",
"type": "File",
"attributes": {
"indentation_level_0+": 4.0,
"indentation_level_1+": 3.0,
"indentation_level_2+": 3.0,
"indentation_level_3+": 3.0,
"indentation_level_4+": 3.0,
"indentation_level_5+": 1.0,
"indentation_level_6+": 0.0
},
"link": "",
"children": []
}
]
},
{
"name": "tabs.included",
"type": "File",
"attributes": {
"indentation_level_0+": 6.0,
"indentation_level_1+": 3.0,
"indentation_level_2+": 1.0,
"indentation_level_3+": 1.0,
"indentation_level_4+": 0.0,
"indentation_level_5+": 0.0,
"indentation_level_6+": 0.0
},
"link": "",
"children": []
}
]
}
],
"apiVersion": "1.3",
"edges": [],
"attributeTypes": {},
"attributeDescriptors": {},
"blacklist": []
}
}