Skip to content

Commit

Permalink
Feature/1886/Add-option-path-column-name-to-csv-importer (#3026)
Browse files Browse the repository at this point in the history
* Add option to csv importer to specify the path column name
#1886

* Add unit test
#1886

* Remove comment and adjust file names
#1886

* Update csv parser dialog #1886

* Add unit test to csv dialog #1886

* Rewrite CSVBuilderTest and change to junit
#1886

* Update CHANGELOG.md #1886

* Reformat code #1886

* Update documentation #1886

Co-authored-by: jannikolai.rueckert <jan.nr@live.de>
Co-authored-by: Jan N. Rückert <31436472+jannikr@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 7, 2022
1 parent 9403343 commit f8edbdf
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 114 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
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
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
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
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
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
@@ -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)
}
})
}
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

0 comments on commit f8edbdf

Please sign in to comment.