Skip to content

Commit

Permalink
fix/3327/fix-parser-suggestions-slow (#3329)
Browse files Browse the repository at this point in the history
* Speed up parser suggestions #3327
  • Loading branch information
moritz-suckow-mw authored Jun 7, 2023
1 parent 7802f2a commit f48693b
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 64 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/)
<img src="https://user-images.githubusercontent.com/65733509/241383211-d9e8e54b-6b06-45bb-8b99-81cc8e0a4596.png" width="450px"/> <img src="https://github.com/MaibornWolff/codecharta/assets/65733509/0ade9ad4-e60b-4911-aadc-d8142167b21a" width="300px"/>
- Expand and restructure documentation regarding Docker usage [#3312](https://github.com/MaibornWolff/codecharta/pull/3312)
- Add current working directories as hint or default value to interactive parser and parser suggestions when asking for input [#3319](https://github.com/MaibornWolff/codecharta/pull/3319)
- Add helpful status messages when calculating parser suggestions [#3329](https://github.com/MaibornWolff/codecharta/pull/3329)

### Fixed 🐞

- Speed up parser suggestions significantly [#3329](https://github.com/MaibornWolff/codecharta/pull/3329)
- Fix color range reset not triggering on color metric change [#3311](https://github.com/MaibornWolff/codecharta/pull/3311)

## [1.117.0] - 2023-05-19
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,8 @@ class GitLogParser(

override fun getDialog(): ParserDialogInterface = ParserDialog
override fun isApplicable(resourceToBeParsed: String): Boolean {
return ResourceSearchHelper.isResourcePresent(resourceToBeParsed, ".git",
ResourceSearchHelper::doesStringEndWith, 1,
shouldSearchFullDirectory = false, resourceShouldBeFile = false)
println("Checking if GitLogParser is applicable...")
return ResourceSearchHelper.isFolderDirectlyInGivenDirectory(resourceToBeParsed, ".git")
}
override fun getName(): String {
return InteractiveParserHelper.GitLogParserConstants.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,8 @@ class MetricGardenerImporter(
override fun getDialog(): ParserDialogInterface = ParserDialog
override fun isApplicable(resourceToBeParsed: String): Boolean {
val supportedLanguageFileEndings = getSupportedLanguageFileEndings()

for (supportedLanguageFileEnding in supportedLanguageFileEndings) {
if (ResourceSearchHelper.isResourcePresent(resourceToBeParsed, supportedLanguageFileEnding,
ResourceSearchHelper::doesStringEndWith, 0,
shouldSearchFullDirectory = true, resourceShouldBeFile = true)) {
return true
}
}

return false
println("Checking if MetricGardener is applicable...")
return ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(resourceToBeParsed, supportedLanguageFileEndings)
}

override fun getName(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,8 @@ class SVNLogParser(

override fun getDialog(): ParserDialogInterface = ParserDialog
override fun isApplicable(resourceToBeParsed: String): Boolean {
return ResourceSearchHelper.isResourcePresent(resourceToBeParsed, ".svn",
ResourceSearchHelper::doStringsEqual, 1,
shouldSearchFullDirectory = false, resourceShouldBeFile = false)
println("Checking if SVNLogParser is applicable...")
return ResourceSearchHelper.isFolderDirectlyInGivenDirectory(resourceToBeParsed, ".svn")
}

override fun getName(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface
import de.maibornwolff.codecharta.tools.interactiveparser.util.InteractiveParserHelper
import de.maibornwolff.codecharta.util.ResourceSearchHelper
import picocli.CommandLine
import java.io.File
import java.io.InputStream
import java.io.PrintStream
import java.net.URL
Expand Down Expand Up @@ -96,15 +97,31 @@ class SonarImporterMain(

override fun getDialog(): ParserDialogInterface = ParserDialog
override fun isApplicable(resourceToBeParsed: String): Boolean {
val trimmedInput = resourceToBeParsed.trim()
println("Checking if SonarImporter is applicable...")

if (trimmedInput.contains("^http(s)?://".toRegex())) {
val trimmedInput = resourceToBeParsed.trim()
val inputFile = File(trimmedInput)
if (isUrl(trimmedInput) || isSonarPropertiesFile(inputFile)) {
return true
}
if (!ResourceSearchHelper.isSearchableDirectory(inputFile)) {
return false
}

return inputFile.walk()
.maxDepth(2)
.asSequence()
.filter { isSonarPropertiesFile(it) }
.any()
}

private fun isUrl(inputString: String): Boolean {
return inputString.contains("^http(s)?://".toRegex())
}

return ResourceSearchHelper.isResourcePresent(trimmedInput, "sonar-project.properties",
ResourceSearchHelper::doStringsEqual, 0,
shouldSearchFullDirectory = true, resourceShouldBeFile = true)
private fun isSonarPropertiesFile(inputFile: File): Boolean {
val searchFile = "sonar-project.properties"
return (inputFile.isFile && inputFile.name == searchFile)
}

override fun getName(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ class SonarImporterMainTest {
fun provideValidInputFiles(): List<Arguments> {
return listOf(
Arguments.of("src/test/resources/my/sonar/repo"),
Arguments.of("src/test/resources/my/sonar/repo/sonar-project.properties"),
Arguments.of("src/test/resources/my/sonar"))
Arguments.of("src/test/resources/my/sonar"),
Arguments.of("src/test/resources/my/sonar/repo/sonar-project.properties"))
}

@JvmStatic
fun provideInvalidInputFiles(): List<Arguments> {
return listOf(
Arguments.of("src/test/resources/my/nonsonar/repo"),
Arguments.of("src/test/resources/my/other/repo"),
Arguments.of("src/test/resources"),
Arguments.of("src/test/resources/my/other/sonar-project.properties"),
Arguments.of("src/test/resources/this/does/not/exist"),
Arguments.of(""))
}
Expand Down Expand Up @@ -132,4 +134,16 @@ class SonarImporterMainTest {
val isApplicable = SonarImporterMain().isApplicable(resourceToBeParsed)
Assertions.assertFalse(isApplicable)
}

@Test
fun `should NOT be identified as applicable if input is a file but not the sonar properties file`() {
val isApplicable = SonarImporterMain().isApplicable("src/test/resources/example.xml")
Assertions.assertFalse(isApplicable)
}

@Test
fun `should NOT be identified as applicable if input does not contain sonar properties file in first two directory levels`() {
val isApplicable = SonarImporterMain().isApplicable("src/test/resources/my")
Assertions.assertFalse(isApplicable)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,8 @@ class SourceCodeParserMain(

override fun getDialog(): ParserDialogInterface = ParserDialog
override fun isApplicable(resourceToBeParsed: String): Boolean {
return ResourceSearchHelper.isResourcePresent(resourceToBeParsed, ".java",
ResourceSearchHelper::doesStringEndWith, 0,
shouldSearchFullDirectory = true, resourceShouldBeFile = true)
println("Checking if SourceCodeParser is applicable...")
return ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(resourceToBeParsed, listOf(".java"))
}

override fun getName(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,66 @@ import java.io.File
class ResourceSearchHelper {

companion object {
fun isResourcePresent(resourceName: String, searchToken: String, searchOperator: (String, String) -> Boolean,
maxSearchingDepth: Int, shouldSearchFullDirectory: Boolean, resourceShouldBeFile: Boolean): Boolean {
val trimmedResourceName = resourceName.trim()

if (trimmedResourceName == "") {
fun isFolderDirectlyInGivenDirectory(directoryPath: String, toBeSearchedFolder: String): Boolean {
val inputFile = getFileFromStringIfExists(directoryPath) ?: return false
if (!inputFile.isDirectory) {
return false
} else if (isInputFileNameSearchToken(inputFile, toBeSearchedFolder)) {
return true
}

// To be able to generally search for the existence of files, do not check empty string here,
// otherwise the real check never gets executed.
if (searchOperator(trimmedResourceName, searchToken) && searchToken != "") {
println("Did not find folder directly, scanning directory `${inputFile.absolutePath}` if folder exists at top level.")
return inputFile.walk()
.maxDepth(1)
.asSequence()
.filter { it.isDirectory && isInputFileNameSearchToken(it, toBeSearchedFolder) }
.any()
}

fun isFileWithOneOrMoreOfEndingsPresent(resourcePath: String, toBeCheckedFileEndings: List<String>): Boolean {
val inputFile = getFileFromStringIfExists(resourcePath) ?: return false
if (inputFile.isFile && endsWithAtLeastOne(inputFile.name, toBeCheckedFileEndings)) {
return true
}

val searchFile = File(trimmedResourceName)
if (!inputFile.isDirectory) {
return false
}

return isResourcePresentInDirectory(searchFile, searchToken, searchOperator, maxSearchingDepth, shouldSearchFullDirectory, resourceShouldBeFile)
println("Given resource did not end with any of the supplied file endings. " +
"Scanning directory `${inputFile.absolutePath}` if it contains a file with any of the supplied file endings.")
return inputFile.walk()
.asSequence()
.filter { it.isFile && endsWithAtLeastOne(it.name, toBeCheckedFileEndings) }
.any()
}

fun doesStringEndWith(toBeCheckedString: String, searchToken: String): Boolean {
return (toBeCheckedString.endsWith(searchToken))
fun isSearchableDirectory(inputFile: File): Boolean {
return(inputFile.isDirectory && inputFile.name != "")
}

fun doStringsEqual(string1: String, string2: String): Boolean {
return (string1 == string2)
private fun getFileFromStringIfExists(inputFilePath: String): File? {
if (inputFilePath == "") {
return null
}
val result = File(inputFilePath.trim())
if (result.exists()) {
return result
}
return null
}

private fun isResourcePresentInDirectory(searchFile: File, searchToken: String, searchOperator: (String, String) -> Boolean,
maxSearchingDepth: Int, shouldSearchFullDirectory: Boolean, resourceShouldBeFile: Boolean): Boolean {
var fileSearch = searchFile.walk()

if (!shouldSearchFullDirectory) {
fileSearch = fileSearch.maxDepth(maxSearchingDepth)
}
private fun isInputFileNameSearchToken(inputFile: File, searchToken: String): Boolean {
return(inputFile.name == searchToken)
}

return if (resourceShouldBeFile) {
fileSearch.asSequence()
.filter { it.isFile }
.map { it.name }
.filter { searchOperator(it, searchToken) }
.any()
} else {
fileSearch.asSequence()
.map { it.name }
.filter { searchOperator(it, searchToken) }
.any()
private fun endsWithAtLeastOne(inputString: String, endings: List<String>): Boolean {
for (ending in endings) {
if (inputString.endsWith(ending)) {
return true
}
}
return false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package de.maibornwolff.codecharta.util

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

class ResourceSearchHelperTest {
// Tests for both functions
@Test
fun `should return false if given directory path does not exist`() {
val input = "src/test/resources/my/doesNotExist"

val resultFolder = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "dummyVal")
val resultFile = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf("dummyVal"))

Assertions.assertThat(resultFolder).isFalse()
Assertions.assertThat(resultFile).isFalse()
}

@Test
fun `should return false if input is empty string`() {
val input = ""

val resultFolder = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "dummyVal")
val resultFile = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf("dummyVal"))

Assertions.assertThat(resultFolder).isFalse()
Assertions.assertThat(resultFile).isFalse()
}

// Tests for `isFolderDirectlyInGivenDirectory`
@Test
fun `should return true if folder exists in given directory path`() {
val input = "src/test/resources/"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "my")

Assertions.assertThat(result).isTrue()
}

@Test
fun `should return true if given directory path is looked for folder`() {
val input = "src/test/resources/my"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "my")

Assertions.assertThat(result).isTrue()
}

@Test
fun `should return false if input is no directory`() {
val input = "src/test/resources/my/java/repo/dummyFile.java"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "dummyFile.java")

Assertions.assertThat(result).isFalse()
}

@Test
fun `should return false if given directory path does not contain looked for folder`() {
val input = "src/test/resources/"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "doesNotExist")

Assertions.assertThat(result).isFalse()
}

@Test
fun `should return false if given directory path does not contain looked for folder at root level`() {
val input = "src/test/resources/my/"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "repo")

Assertions.assertThat(result).isFalse()
}

@Test
fun `should return false if given directory path does only contain file with looked for name`() {
val input = "src/test/resources/my/java/repo"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, "dummyFile.java")

Assertions.assertThat(result).isFalse()
}

// Tests for `isFileWithOneOrMoreOfEndingsPresent`
@Test
fun `should return true if resource ends with one of the given endings and is file`() {
val input = "src/test/resources/my/java/repo/dummyFile.java"

val result = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf(".java"))

Assertions.assertThat(result).isTrue()
}

@Test
fun `should return true if resource is directory and contains file ending with one of the given file endings`() {
val input = "src/test/resources/my/"

val result = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf(".java"))

Assertions.assertThat(result).isTrue()
}

@Test
fun `should return false if input does not end with given file endings and is no directory`() {
val input = "src/test/resources/my/java/repo/dummyFile.java"

val result = ResourceSearchHelper.isFolderDirectlyInGivenDirectory(input, ".html")

Assertions.assertThat(result).isFalse()
}

@Test
fun `should return false if resource is directory and contains no file ending with one of the given file endings`() {
val input = "src/test/resources/my/other"

val result = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf(".java"))

Assertions.assertThat(result).isFalse()
}

@Test
fun `should return false if resource is directory ending with one of the given file endings`() {
val input = "src/test/resources/my/other/.java"

val result = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf(".java"))

Assertions.assertThat(result).isFalse()
}

@Test
fun `should return false if resource is directory and contains another directory ending with one of the given file endings`() {
val input = "src/test/resources/my/other/"

val result = ResourceSearchHelper.isFileWithOneOrMoreOfEndingsPresent(input, listOf(".java"))

Assertions.assertThat(result).isFalse()
}
}
Empty file.
Empty file.
Loading

0 comments on commit f48693b

Please sign in to comment.