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 file extension for merged project #3409 #3421

Merged
merged 5 commits into from
Nov 28, 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 @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/)
- 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)
- Fix the csv-exporter so that it exports multiple projects instead of just one when multiple projects are specified [#3414](https://github.com/MaibornWolff/codecharta/pull/3414)
- Fix file extensions of output files for merged projects [#3421](https://github.com/MaibornWolff/codecharta/pull/3421)

### Added 🚀

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ object OutputFileHandler {
if (compressed) FileExtension.GZIP.extension else String()
FileExtension.CSV -> outputName
.removeSuffix(FileExtension.CSV.extension) +
FileExtension.CSV.extension
FileExtension.CSV.extension
else -> throw IllegalArgumentException()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class Ccsh : Callable<Void?> {
val mergeArguments =
listOf(
ccJsonFilePath,
"--output-file=$outputFilePath+",
"--output-file=$outputFilePath",
"--not-compressed=true",
"--add-missing=false",
"--recursive=true",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import de.maibornwolff.codecharta.tools.ccsh.Ccsh
import de.maibornwolff.codecharta.tools.ccsh.parser.InteractiveParserSuggestionDialog
import de.maibornwolff.codecharta.tools.ccsh.parser.ParserService
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.verify
import mu.KLogger
import mu.KotlinLogging
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import picocli.CommandLine
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
Expand All @@ -42,7 +45,7 @@ class CcshTest {
System.setErr(originalErr)
}

@AfterAll
@AfterEach
fun afterTest() {
unmockkAll()
}
Expand Down Expand Up @@ -99,35 +102,47 @@ class CcshTest {
}

@Test
fun `should convert arguments to kebab-case`() {
fun `should convert all arguments to snake-case when given arguments`() {
// given
val outStream = ByteArrayOutputStream()
val originalErr = System.err
System.setErr(PrintStream(outStream))

// when
val exitCode = Ccsh.executeCommandLine(arrayOf("edgefilter", ".", "--defaultExcludesS=AbC"))

Assertions.assertThat(exitCode).isNotZero
// then
Assertions.assertThat(exitCode).isNotZero()
Assertions.assertThat(outStream.toString()).contains("--default-excludes-s=AbC")

// clean up
System.setErr(originalErr)
}

@Test
fun `should just show help message`() {
fun `should only show help message when executed with help flag`() {
// given
mockkObject(ParserService)
val outStream = ByteArrayOutputStream()
val originalOut = System.out
System.setOut(PrintStream(outStream))

// when
val exitCode = Ccsh.executeCommandLine(arrayOf("-h"))

// then
Assertions.assertThat(exitCode).isEqualTo(0)
Assertions.assertThat(outStream.toString())
.contains("Usage: ccsh [-hiv] [COMMAND]", "Command Line Interface for CodeCharta analysis")
verify(exactly = 0) { ParserService.executePreconfiguredParser(any(), any()) }

// clean up
System.setOut(originalOut)
}

@Test
fun `should execute parser suggestions and all selected parsers when no options are passed`() {
// given
val selectedParsers = listOf("parser1", "parser2")
val args = listOf(listOf("dummyArg1"), listOf("dummyArg2"))

Expand All @@ -140,98 +155,152 @@ class CcshTest {
KInquirer.promptInput(any(), any(), any(), any(), any())
} returns "dummy/path"

// when
val exitCode = Ccsh.executeCommandLine(emptyArray())
Assertions.assertThat(exitCode).isZero

// then
Assertions.assertThat(exitCode).isZero
// 3 because 2 parsers and the merge in the end
verify(exactly = 3) { ParserService.executePreconfiguredParser(any(), any()) }
}

@Test
fun `should only execute parsers when configuration was successful`() {
// given
mockInteractiveParserSuggestionDialog(emptyList(), emptyList())
mockSuccessfulParserService()

// when
val exitCode = Ccsh.executeCommandLine(emptyArray())

// then
Assertions.assertThat(exitCode == 0).isTrue()
verify(exactly = 0) { ParserService.executeSelectedParser(any(), any()) }
verify(exactly = 0) { ParserService.executePreconfiguredParser(any(), any()) }
}

@Test
fun `should only execute parsers when user does confirm execution`() {
// given
val selectedParsers = listOf("parser1", "parser2")
val args = listOf(listOf("dummyArg1"), listOf("dummyArg2"))

mockInteractiveParserSuggestionDialog(selectedParsers, args)
mockSuccessfulParserService()
mockKInquirerConfirm(false)

// when
val exitCode = Ccsh.executeCommandLine(emptyArray())

// then
Assertions.assertThat(exitCode == 0).isTrue()
verify(exactly = 0) { ParserService.executeSelectedParser(any(), any()) }
verify(exactly = 0) { ParserService.executePreconfiguredParser(any(), any()) }
}

@Test
fun `should continue executing parsers even if there is an error while executing one`() {
fun `should continue executing parsers even when one parser throws an error`() {
// given
mockUnsuccessfulParserService()

val dummyConfiguredParsers =
mapOf("dummyParser1" to listOf("dummyArg1", "dummyArg2"),
"dummyParser2" to listOf("dummyArg1", "dummyArg2"))

// when
Ccsh.executeConfiguredParsers(cmdLine, dummyConfiguredParsers)

// then
verify(exactly = 2) { ParserService.executePreconfiguredParser(any(), any()) }
verify(exactly = 0) { ParserService.executeSelectedParser(any(), any()) }
}

@Test
fun `should execute interactive parser when passed parser is unknown`() {
// given
mockSuccessfulParserService()

// when
val exitCode = Ccsh.executeCommandLine(arrayOf("unknownparser"))
Assertions.assertThat(exitCode).isZero

// then
Assertions.assertThat(exitCode).isZero
verify { ParserService.executeSelectedParser(any(), any()) }
}

@Test
fun `should execute interactive parser when -i option is passed`() {
// given
mockSuccessfulParserService()

// when
val exitCode = Ccsh.executeCommandLine(arrayOf("-i"))
Assertions.assertThat(exitCode).isZero

// then
Assertions.assertThat(exitCode).isZero
verify { ParserService.executeSelectedParser(any(), any()) }
}

@Test
fun `should execute the selected interactive parser when only called with name and no args`() {
// given
mockSuccessfulParserService()

// when
val exitCode = Ccsh.executeCommandLine(arrayOf("sonarimport"))

// then
Assertions.assertThat(exitCode).isEqualTo(0)
Assertions.assertThat(errContent.toString())
.contains("Executing sonarimport")
}

@Test
fun `should not ask for merging results after using parser suggestions if only one parser was executed`() {
fun `should not ask for merging results after using parser suggestions when only one parser was executed`() {
// given
val selectedParsers = listOf("parser1")
val args = listOf(listOf("dummyArg1"))

mockInteractiveParserSuggestionDialog(selectedParsers, args)
mockSuccessfulParserService()
mockKInquirerConfirm(true)

// when
val exitCode = Ccsh.executeCommandLine(emptyArray())

// then
Assertions.assertThat(exitCode).isZero()
Assertions.assertThat(errContent.toString())
.contains("Parser was successfully executed.")
}

@Test
fun `should log the absolute path of the merged output file when asked to merge results`() {
// given
val folderPath = "src/test/resources"
val outputFilePath = "$folderPath/mergedResult.cc.json.gz"
val absoluteOutputFilePath = File(outputFilePath).absolutePath
val outputFile = File(outputFilePath)
outputFile.deleteOnExit()
val multipleConfiguredParsers = mapOf("dummyParser1" to listOf("dummyArg1", "dummyArg2"), "dummyParser2" to listOf("dummyArg1", "dummyArg2"))

val loggerMock = mockk<KLogger>()
val infoMessagesLogged = mutableListOf<String>()
mockkObject(KotlinLogging)
every { KotlinLogging.logger(any<(() -> Unit)>()) } returns loggerMock
every { loggerMock.info(capture(infoMessagesLogged)) } returns Unit

mockKInquirerConfirm(true)

mockkStatic("com.github.kinquirer.components.InputKt")
every {
KInquirer.promptInput(any(), any(), any(), any(), any())
} returns folderPath

// when
Ccsh.executeConfiguredParsers(cmdLine, multipleConfiguredParsers)

// then
Assertions.assertThat(infoMessagesLogged.any { e -> e.endsWith(absoluteOutputFilePath) }).isTrue()
verify { loggerMock.info(any<String>()) }
}
}