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/1886/Add-option-path-column-name-to-csv-importer #3026

Merged
merged 12 commits into from
Sep 7, 2022
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 👨‍💻 👩‍💻)

### Added 🚀

- Add option to CSVImporter to specify the path column name [#3026](https://github.com/MaibornWolff/codecharta/pull/3026)

### Changed

- Stop asking the user to compress a file when printing to stdOut [#3024](https://github.com/MaibornWolff/codecharta/pull/3024)
Expand Down
1 change: 1 addition & 0 deletions analysis/import/CSVImporter/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: junit5_version
testImplementation group: 'org.jetbrains.kotlin', name: 'kotlin-test', version: kotlin_version
testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: hamcrest_version
testImplementation group: 'org.skyscreamer', name: 'jsonassert', version: '1.2.3'
testImplementation group: 'io.mockk', name: 'mockk', version: mockk_version
testImplementation("org.spekframework.spek2:spek-dsl-jvm:$spek2_version") {
exclude group: 'org.jetbrains.kotlin'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ class CSVImporter(
@CommandLine.Parameters(arity = "1..*", paramLabel = "FILE", description = ["sourcemonitor csv files"])
private var files: List<File> = mutableListOf()

@CommandLine.Option(names = ["--path-column-name"], description = ["name of path column"])
private var pathColumnName: String = "path"

@Throws(IOException::class)
override fun call(): Void? {

val csvProjectBuilder = CSVProjectBuilder(pathSeparator, csvDelimiter)
val csvProjectBuilder = CSVProjectBuilder(pathSeparator, csvDelimiter, pathColumnName)
files.map { it.inputStream() }.forEach<InputStream> { csvProjectBuilder.parseCSVStream(it) }
val project = csvProjectBuilder.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ class ParserDialog {
hint = "output.cc.json"
)

val pathColumnName: String = KInquirer.promptInput(
message = "What is the name of the path column name?",
default = "path"
)

val delimiter = KInquirer.promptInput(
message = "Which column delimiter is used in the CSV file?",
hint = ",",
Expand All @@ -47,10 +52,11 @@ class ParserDialog {
)

return inputFileNames + listOfNotNull(
"--output-file=$outputFileName",
"--delimiter=$delimiter",
"--path-separator=$pathSeparator",
if (isCompressed) null else "--not-compressed",
"--output-file=$outputFileName",
"--path-column-name=$pathColumnName",
"--delimiter=$delimiter",
"--path-separator=$pathSeparator",
if (isCompressed) null else "--not-compressed",
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,62 @@ import de.maibornwolff.codecharta.importer.csv.CSVImporter.Companion.main
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.skyscreamer.jsonassert.JSONAssert
import org.skyscreamer.jsonassert.JSONCompareMode
import java.io.File

class CSVImporterTest {

@Test
fun `should create json uncompressed file`() {
main(arrayOf("src/test/resources/sourcemonitor.csv", "-nc",
"-o=src/test/resources/sourcemonitor.cc.json"))
val file = File("src/test/resources/sourcemonitor.cc.json")
main(
arrayOf(
"src/test/resources/csvimporter.csv", "-nc",
"-o=src/test/resources/csvimporter.cc.json"
)
)
val file = File("src/test/resources/csvimporter.cc.json")
file.deleteOnExit()

assertTrue(file.exists())
}

@Test
fun `should create a correct json when having a different path column name`() {
main(
arrayOf(
"src/test/resources/csvimporter_different_path_column_name.csv", "-nc",
"--path-separator=\\",
"--path-column-name=File Name",
"-o=src/test/resources/csvimporter.cc.json"
)
)
val file = File("src/test/resources/csvimporter.cc.json")
val testJsonString = File("src/test/resources/csvimporter.cc.json").readText()
val expectedJsonString = File("src/test/resources/csvimporter_different_path_column_name.cc.json").readText()
file.deleteOnExit()

JSONAssert.assertEquals(testJsonString, expectedJsonString, JSONCompareMode.STRICT)
}

@Test
fun `should create json gzip file`() {
main(arrayOf("src/test/resources/sourcemonitor.csv", "-o=src/test/resources/sourcemonitor.cc.json"))
val file = File("src/test/resources/sourcemonitor.cc.json.gz")
main(arrayOf("src/test/resources/csvimporter.csv", "-o=src/test/resources/csvimporter.cc.json"))
val file = File("src/test/resources/csvimporter.cc.json.gz")
file.deleteOnExit()

assertTrue(file.exists())
}

@Test
fun `should contain Lines value of 44`() {
main(arrayOf("src/test/resources/sourcemonitor.csv", "-nc",
"-o=src/test/resources/sourcemonitor.cc.json"))
val file = File("src/test/resources/sourcemonitor.cc.json")
main(
arrayOf(
"src/test/resources/csvimporter.csv", "-nc",
"-o=src/test/resources/csvimporter.cc.json"
)
)
val file = File("src/test/resources/csvimporter.cc.json")
file.deleteOnExit()

assertThat(file.readText()).contains(listOf("\"Lines\":44.0"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,117 +1,101 @@
package de.maibornwolff.codecharta.importer.csv

import de.maibornwolff.codecharta.translator.MetricNameTranslator
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.hasItem
import org.hamcrest.Matchers.hasSize
import org.hamcrest.Matchers.`is`
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
import org.junit.jupiter.api.Test
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.nio.charset.StandardCharsets
import kotlin.test.assertEquals

class CSVProjectBuilderTest : Spek({
fun toInputStream(content: String): InputStream {
class CSVProjectBuilderTest {

private fun toInputStream(content: String): InputStream {
return ByteArrayInputStream(content.toByteArray(StandardCharsets.UTF_8))
}

describe("a CSVProjectBuilder") {
@Test
fun `ignore invalid csv content`() {
val csvProjectBuilder = CSVProjectBuilder('\\', ',')
val invalidContent = "head,path\nnoValidContent\n"
val project = csvProjectBuilder
.parseCSVStream(toInputStream(invalidContent))
.build()

context("adding invalid csv") {

val invalidContent = "head,path\nnoValidContent\n"
val project = csvProjectBuilder
.parseCSVStream(toInputStream(invalidContent))
.build()

it("should be ignored") {
assertThat(project.rootNode.children, hasSize(0))
}
}
assertEquals(project.rootNode.children.size, 0)
}

context("adding valid csv with usual line breaks") {
val name = "someName"
val project = csvProjectBuilder.parseCSVStream(
toInputStream("someContent,,path\nprojectName,blubb2,$name")
)
.build()
@Test
fun `adding valid csv with unix line breaks`() {
val csvProjectBuilder = CSVProjectBuilder('\\', ',')
val name = "someName"
val project = csvProjectBuilder.parseCSVStream(
toInputStream("someContent,,path\nprojectName,foo,$name")
)
.build()
val childName = project.rootNode.children.map { it.name }

it("should have node with same name") {
assertThat(project.rootNode.children.map { it.name }, hasItem(name))
}
}
assertEquals(childName[0], name)
}

context("adding valid csv with windows line breaks") {
val name = "someName"
val project = csvProjectBuilder.parseCSVStream(
toInputStream("someContent,,path\r\nprojectName,blubb2,$name")
)
.build()
@Test
fun `adding valid csv with windows line breaks`() {
val csvProjectBuilder = CSVProjectBuilder('\\', ',')
val name = "someName"
val project = csvProjectBuilder.parseCSVStream(
toInputStream("someContent,,path\r\nprojectName,foo,$name")
)
.build()
val childName = project.rootNode.children.map { it.name }

it("should have node with same name") {
assertThat(project.rootNode.children.map { it.name }, hasItem(name))
}
}
assertEquals(childName[0], name)
}

describe("a CSVProjectBuilder") {
@Test
fun `adding line with metric values`() {
val csvProjectBuilder = CSVProjectBuilder('\\', ',')
val attributeName = "attributeName"
val attributeValue = "\"0,1\""
val attributeValueFloat = 0.1

context("adding line with metric values") {
val attribName = "attname"
val attribVal = "\"0,1\""
val attValFloat = 0.1

val project = csvProjectBuilder.parseCSVStream(
toInputStream(
"head1,path,head3,head4,$attribName\nprojectName,\"9900,01\",\"blubb\",1.0,$attribVal\n"
)
val project = csvProjectBuilder.parseCSVStream(
toInputStream(
"head1,path,head3,head4,$attributeName\nprojectName,\"9900,01\",\"foo\",1.0,$attributeValue\n"
)
.build()

it("should add attributes to node") {
val nodeAttributes = project.rootNode.children.iterator().next().attributes
assertThat(nodeAttributes.size, `is`(3))
assertThat<Any>(nodeAttributes[attribName], `is`<Any>(attValFloat))
}
}
)
.build()

val nodeAttributes = project.rootNode.children.iterator().next().attributes

assertEquals(nodeAttributes.size, 3)
assertEquals(nodeAttributes[attributeName], attributeValueFloat)
}

describe("a CSVProjectBuilder") {
@Test
fun `adding file with subdirectory`() {
val csvProjectBuilder = CSVProjectBuilder('\\', ',')
val directoryName = "someNodeName"
val project = csvProjectBuilder
.parseCSVStream(toInputStream("someContent\n$directoryName\\someFile"))
.build()

assertEquals(project.rootNode.children.size, 1)

context("adding file with subdirectory") {
val directoryName = "someNodeName"
val project = csvProjectBuilder
.parseCSVStream(toInputStream("someContent\n$directoryName\\someFile"))
.build()

it("should create node for subdirectory") {
assertThat(project.rootNode.children.size, `is`(1))
val node = project.rootNode.children.iterator().next()
assertThat(node.name, `is`(directoryName))
assertThat(node.children.size, `is`(1))
}
}
val node = project.rootNode.children.iterator().next()

assertEquals(node.name, directoryName)
assertEquals(node.children.size, 1)
}

describe("CSVProjectBuilder for Sourcemonitor") {
@Test
fun `reading csv lines from Sourcemonitor`() {
val csvProjectBuilder = CSVProjectBuilder(
'\\',
',',
metricNameTranslator = MetricNameTranslator(mapOf(Pair("File Name", "path")))
"File Name"
)
val project = csvProjectBuilder
.parseCSVStream(this.javaClass.classLoader.getResourceAsStream("sourcemonitor.csv"))
.build()

context("reading csv lines from Sourcemonitor") {
val project = csvProjectBuilder
.parseCSVStream(this.javaClass.classLoader.getResourceAsStream("sourcemonitor.csv"))
.build()

it("has correct number of nodes") {
assertThat(project.size, `is`(39))
}
}
assertEquals(39, project.size)
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ class ParserDialogTest {
fun `should output correct arguments`() {
val fileName = "in.csv"
val outputFileName = "out.cc.json"
val pathColumnName = "path"
val delimiter = ";"
val pathSeparator = "/"
val isCompressed = false

mockkStatic("com.github.kinquirer.components.InputKt")
every {
KInquirer.promptInput(any(), any(), any(), any())
} returns fileName andThen "" andThen outputFileName andThen delimiter andThen pathSeparator
KInquirer.promptInput(any(), any(), any(), any(), any())
} returns fileName andThen "" andThen outputFileName andThen pathColumnName andThen delimiter andThen pathSeparator
mockkStatic("com.github.kinquirer.components.ConfirmKt")
every {
KInquirer.promptConfirm(any(), any())
Expand All @@ -43,7 +44,9 @@ class ParserDialogTest {

val cmdLine = CommandLine(CSVImporter())
val parseResult = cmdLine.parseArgs(*parserArguments.toTypedArray())
Assertions.assertThat(parseResult.matchedOption("output-file").getValue<String>().equals(outputFileName))
Assertions.assertThat(parseResult.matchedOption("output-file").getValue<String>()
.equals(outputFileName))
Assertions.assertThat(parseResult.matchedOption("path-column-name").getValue<String>()).isEqualTo(pathColumnName)
Assertions.assertThat(parseResult.matchedOption("delimiter").getValue<Char>()).isEqualTo(delimiter[0])
Assertions.assertThat(parseResult.matchedOption("path-separator").getValue<Char>()).isEqualTo(pathSeparator[0])
Assertions.assertThat(parseResult.matchedOption("not-compressed").getValue<Boolean>()).isEqualTo(isCompressed)
Expand All @@ -56,14 +59,15 @@ class ParserDialogTest {
val fileName2 = "in2.csv"
val fileName3 = "in3.csv"
val outputFileName = "out.cc.json"
val pathColumnName = "path"
val delimiter = ";"
val pathSeparator = "/"
val isCompressed = true

mockkStatic("com.github.kinquirer.components.InputKt")
every {
KInquirer.promptInput(any(), any(), any(), any(), any(), any())
} returns fileName andThen fileName2 andThen fileName3 andThen "" andThen outputFileName andThen delimiter andThen pathSeparator
} returns fileName andThen fileName2 andThen fileName3 andThen "" andThen outputFileName andThen pathColumnName andThen delimiter andThen pathSeparator
mockkStatic("com.github.kinquirer.components.ConfirmKt")
every {
KInquirer.promptConfirm(any(), any())
Expand All @@ -74,6 +78,7 @@ class ParserDialogTest {
val cmdLine = CommandLine(CSVImporter())
val parseResult = cmdLine.parseArgs(*parserArguments.toTypedArray())
Assertions.assertThat(parseResult.matchedOption("output-file").getValue<String>().equals(outputFileName))
Assertions.assertThat(parseResult.matchedOption("path-column-name").getValue<String>()).isEqualTo(pathColumnName)
Assertions.assertThat(parseResult.matchedOption("delimiter").getValue<Char>()).isEqualTo(delimiter[0])
Assertions.assertThat(parseResult.matchedOption("path-separator").getValue<Char>()).isEqualTo(pathSeparator[0])
assertNull(parseResult.matchedOption("not-compressed"))
Expand Down