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/3338/improve-parser-suggestion-1 #3384

Merged
merged 6 commits into from
Oct 30, 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
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 🚀

- Only ask to merge results after parser suggestion execution when more than one parser was executed [#3384](https://github.com/MaibornWolff/codecharta/pull/3384)

### Fixed 🐞

- Fix command not found issue for --version and --help in the analysis [#3377](https://github.com/MaibornWolff/codecharta/pull/3377)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,25 @@ class Ccsh : Callable<Void?> {
val currentExitCode = executeConfiguredParser(commandLine, configuredParser)
if (currentExitCode != 0) {
exitCode.set(currentExitCode)
logger.info("Code: $currentExitCode")
logger.info { "Code: $currentExitCode" }
}
}
}
threadPool.shutdown()
threadPool.awaitTermination(1, TimeUnit.DAYS)

val finalExitCode = exitCode.get()
logger.info("Code: $finalExitCode")
logger.info { "Code: $finalExitCode" }
if (finalExitCode != 0) {
return finalExitCode
}
if (configuredParsers.size == 1) {
logger.info { "Parser was successfully executed and created a cc.json file." }
return 0
}

// Improvement: Try to extract merge commands before so user does not have to configure merge args?
logger.info("Each parser was successfully executed and created a cc.json file.")
logger.info { "Each parser was successfully executed and created a cc.json file." }
return askAndMergeResults(commandLine)
}

Expand Down Expand Up @@ -178,7 +183,7 @@ class Ccsh : Callable<Void?> {
val exitCode = ParserService.executePreconfiguredParser(commandLine, Pair(configuredParser.key, configuredParser.value))

if (exitCode != 0) {
logger.info("Error executing ${configuredParser.key}, code $exitCode")
logger.info { "Error executing ${configuredParser.key}, code $exitCode" }
}

return exitCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import io.mockk.unmockkAll
import io.mockk.verify
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
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
Expand All @@ -24,26 +25,79 @@ import java.io.PrintStream
class CcshTest {
private val outContent = ByteArrayOutputStream()
private val originalOut = System.out
val errContent = ByteArrayOutputStream()
val originalErr = System.err
private val errContent = ByteArrayOutputStream()
private val originalErr = System.err

private val cmdLine = CommandLine(Ccsh())

@BeforeAll
fun setUpStreams() {
@BeforeEach
fun setupStreams() {
System.setOut(PrintStream(outContent))
System.setErr(PrintStream(errContent))
}

@AfterAll
@AfterEach
fun restoreStreams() {
System.setOut(originalOut)
System.setErr(originalErr)
}

@AfterAll
fun afterTest() {
unmockkAll()
}

private fun mockSuccessfulParserService() {
mockkObject(ParserService)
every {
ParserService.selectParser(any(), any())
} returns "someparser"

every {
ParserService.executeSelectedParser(any(), any())
} returns 0

every {
ParserService.executePreconfiguredParser(any(), any())
} returns 0
}

private fun mockUnsuccessfulParserService() {
mockkObject(ParserService)

every {
ParserService.executeSelectedParser(any(), any())
} returns -1

every {
ParserService.executePreconfiguredParser(any(), any())
} returns -1
}

private fun mockInteractiveParserSuggestionDialog(selectedParsers: List<String>,
parserArgs: List<List<String>>) {
if (selectedParsers.size != parserArgs.size) {
throw IllegalArgumentException("There must be the same amount of args as parsers!")
}

val parsersAndArgs = mutableMapOf<String, List<String>>()
selectedParsers.zip(parserArgs) { currentParserName, currentParserArgs ->
parsersAndArgs.put(currentParserName, currentParserArgs)
}

mockkObject(InteractiveParserSuggestionDialog)
every {
InteractiveParserSuggestionDialog.offerAndGetInteractiveParserSuggestionsAndConfigurations(any())
} returns parsersAndArgs
}

private fun mockKInquirerConfirm(shouldConfirm: Boolean) {
mockkStatic("com.github.kinquirer.components.ConfirmKt")
every {
KInquirer.promptConfirm(any(), any())
} returns shouldConfirm
}

@Test
fun `should convert arguments to kebab-case`() {
val outStream = ByteArrayOutputStream()
Expand Down Expand Up @@ -77,20 +131,9 @@ class CcshTest {
val selectedParsers = listOf("parser1", "parser2")
val args = listOf(listOf("dummyArg1"), listOf("dummyArg2"))

mockkObject(InteractiveParserSuggestionDialog)
every {
InteractiveParserSuggestionDialog.offerAndGetInteractiveParserSuggestionsAndConfigurations(any())
} returns mapOf(selectedParsers[0] to args[0], selectedParsers[1] to args[1])

mockkObject(ParserService)
every {
ParserService.executePreconfiguredParser(any(), any())
} returns 0

mockkStatic("com.github.kinquirer.components.ConfirmKt")
every {
KInquirer.promptConfirm(any(), any())
} returns true
mockInteractiveParserSuggestionDialog(selectedParsers, args)
mockSuccessfulParserService()
mockKInquirerConfirm(true)

mockkStatic("com.github.kinquirer.components.InputKt")
every {
Expand All @@ -106,18 +149,8 @@ class CcshTest {

@Test
fun `should only execute parsers when configuration was successful`() {
mockkObject(InteractiveParserSuggestionDialog)
every {
InteractiveParserSuggestionDialog.offerAndGetInteractiveParserSuggestionsAndConfigurations(any())
} returns emptyMap()

mockkObject(ParserService)
every {
ParserService.executeSelectedParser(any(), any())
} returns 0
every {
ParserService.executePreconfiguredParser(any(), any())
} returns 0
mockInteractiveParserSuggestionDialog(emptyList(), emptyList())
mockSuccessfulParserService()

val exitCode = Ccsh.executeCommandLine(emptyArray())

Expand All @@ -131,23 +164,9 @@ class CcshTest {
val selectedParsers = listOf("parser1", "parser2")
val args = listOf(listOf("dummyArg1"), listOf("dummyArg2"))

mockkObject(InteractiveParserSuggestionDialog)
every {
InteractiveParserSuggestionDialog.offerAndGetInteractiveParserSuggestionsAndConfigurations(any())
} returns mapOf(selectedParsers[0] to args[0], selectedParsers[1] to args[1])

mockkObject(ParserService)
every {
ParserService.executeSelectedParser(any(), any())
} returns 0
every {
ParserService.executePreconfiguredParser(any(), any())
} returns 0

mockkStatic("com.github.kinquirer.components.ConfirmKt")
every {
KInquirer.promptConfirm(any(), any())
} returns false
mockInteractiveParserSuggestionDialog(selectedParsers, args)
mockSuccessfulParserService()
mockKInquirerConfirm(false)

val exitCode = Ccsh.executeCommandLine(emptyArray())

Expand All @@ -158,16 +177,11 @@ class CcshTest {

@Test
fun `should continue executing parsers even if there is an error while executing one`() {
mockkObject(ParserService)
every {
ParserService.executeSelectedParser(any(), any())
} returns -1
every {
ParserService.executePreconfiguredParser(any(), any())
} returns -1

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

val dummyConfiguredParsers =
mapOf("dummyParser1" to listOf("dummyArg1", "dummyArg2"),
"dummyParser2" to listOf("dummyArg1", "dummyArg2"))
Ccsh.executeConfiguredParsers(cmdLine, dummyConfiguredParsers)

verify(exactly = 2) { ParserService.executePreconfiguredParser(any(), any()) }
Expand All @@ -176,13 +190,7 @@ class CcshTest {

@Test
fun `should execute interactive parser when passed parser is unknown`() {
mockkObject(ParserService)
every {
ParserService.selectParser(any(), any())
} returns "someparser"
every {
ParserService.executeSelectedParser(any(), any())
} returns 0
mockSuccessfulParserService()

val exitCode = Ccsh.executeCommandLine(arrayOf("unknownparser"))
Assertions.assertThat(exitCode).isZero
Expand All @@ -192,13 +200,7 @@ class CcshTest {

@Test
fun `should execute interactive parser when -i option is passed`() {
mockkObject(ParserService)
every {
ParserService.selectParser(any(), any())
} returns "someparser"
every {
ParserService.executeSelectedParser(any(), any())
} returns 0
mockSuccessfulParserService()

val exitCode = Ccsh.executeCommandLine(arrayOf("-i"))
Assertions.assertThat(exitCode).isZero
Expand All @@ -208,17 +210,28 @@ class CcshTest {

@Test
fun `should execute the selected interactive parser when only called with name and no args`() {
mockkObject(ParserService)
every {
ParserService.executeSelectedParser(any(), any())
} returns 0
mockSuccessfulParserService()

System.setErr(PrintStream(errContent))
val exitCode = Ccsh.executeCommandLine(arrayOf("sonarimport"))
System.setErr(originalErr)

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`() {
val selectedParsers = listOf("parser1")
val args = listOf(listOf("dummyArg1"))

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

val exitCode = Ccsh.executeCommandLine(emptyArray())

Assertions.assertThat(exitCode).isZero()
Assertions.assertThat(errContent.toString())
.contains("Parser was successfully executed and created a cc.json file.")
}
}