diff --git a/save-common/src/commonMain/kotlin/com/saveourtool/save/core/files/FileUtils.kt b/save-common/src/commonMain/kotlin/com/saveourtool/save/core/files/FileUtils.kt index d8f335848..6f11cc420 100644 --- a/save-common/src/commonMain/kotlin/com/saveourtool/save/core/files/FileUtils.kt +++ b/save-common/src/commonMain/kotlin/com/saveourtool/save/core/files/FileUtils.kt @@ -217,6 +217,19 @@ fun Path.parentsWithSelf() = listOf(this) + this.parents().toList() */ expect fun FileSystem.myDeleteRecursively(path: Path) +/** + * Find a file in any of parent directories and return this directory + * + * @param path path for which ancestors should be checked + * @param fileName a name of the file that will be searched for + * @return a path to one of parent directories or null if no directory contains [fileName] + */ +fun FileSystem.findAncestorDirContainingFile(path: Path, fileName: String): Path? = path.parents().firstOrNull { parent -> + metadata(parent).isDirectory && list(parent).any { + it.name == fileName + } +} + /** * @return current working directory */ diff --git a/save-common/src/commonMain/kotlin/com/saveourtool/save/core/utils/SarifFileUtils.kt b/save-common/src/commonMain/kotlin/com/saveourtool/save/core/utils/SarifFileUtils.kt new file mode 100644 index 000000000..cfc9e9f84 --- /dev/null +++ b/save-common/src/commonMain/kotlin/com/saveourtool/save/core/utils/SarifFileUtils.kt @@ -0,0 +1,61 @@ +/** + * Utility methods to work with SARIF files. + */ + +package com.saveourtool.save.core.utils + +import com.saveourtool.save.core.files.findAncestorDirContainingFile +import com.saveourtool.save.core.files.fs +import com.saveourtool.save.core.files.parents +import com.saveourtool.save.core.plugin.PluginException + +import okio.FileSystem +import okio.Path + +/** + * @return string with trimmed `file://` or `file:///` + */ +fun String.dropFileProtocol() = substringAfter("file://") + .let { + // It is a valid format for Windows paths to look like `file:///C:/stuff` + if (it[0] == '/' && it[2] == ':') it.drop(1) else it + } + +/** + * Make all paths in [this] collection relative to [root] + * + * @param root a common root for files in [this] + * @return a list of relative paths + */ +fun List.adjustToCommonRoot(root: Path) = map { + it.relativeTo(root).normalized() +} + +/** + * Find the last parent directory containing save.toml. + * + * @param path a path to start the search + * @return one of parent directories + */ +fun FileSystem.topmostTestDirectory(path: Path): Path = path.parents().last { parent -> + list(parent).any { it.name == "save.toml" } +} + +/** + * Calculate the path to sarif file; we expect, that it single for the all tests and located in one of parent directories + * for evaluated test files + * + * @param sarifFileName sarif file name + * @param anchorTestFilePath anchor file for calculating corresponding sarif file; + * since .sarif file expected to be the one for all test files, it could be any of test file + * @return path to sarif + * @throws PluginException in case of absence of sarif file + */ +fun calculatePathToSarifFile(sarifFileName: String, anchorTestFilePath: Path): Path = fs.findAncestorDirContainingFile( + anchorTestFilePath, sarifFileName +)?.let { + it / sarifFileName +} ?: throw PluginException( + "Could not find SARIF file with expected warnings/fixes for file $anchorTestFilePath. " + + "Please check if correct `FarningsFormat`/`FixFormat` is set (should be SARIF) and if the file is present and called `$sarifFileName`." +) diff --git a/save-plugins/fix-plugin/README.md b/save-plugins/fix-plugin/README.md index 9d2fb31f3..e8f73be56 100644 --- a/save-plugins/fix-plugin/README.md +++ b/save-plugins/fix-plugin/README.md @@ -1,5 +1,11 @@ ## Save fix plugin Plugin that runs provided executable on the initial file with a test source code and compares its output with an expected result. + +Fix plugin supports two types of execution: `IN_PLACE` and `SARIF`, which could be specified by `actualFixFormat` flag. +In case of `IN_PLACE` mode, `save` will apply fixes, obtained by static analysis tool by executing it with provided configuration, +while in `SARIF` mode, it will expect the `.sarif` file, with the list of fixes, which could be provided by `actualFixSarifFileName` flag. +Plugin will extract all fixes from sarif and apply them to the test files. More information about sarif fix sections could be found [here](https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317881). + Please note, that it is important for test resources to have specific postfixes. By the default test file it should be `Test` , for the file with expected result - it should be `Expected`. diff --git a/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPlugin.kt b/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPlugin.kt index 0d241b663..ac561a0cb 100644 --- a/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPlugin.kt +++ b/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPlugin.kt @@ -23,6 +23,7 @@ import com.saveourtool.save.core.utils.ExecutionResult import com.saveourtool.save.core.utils.PathSerializer import com.saveourtool.save.core.utils.ProcessExecutionException import com.saveourtool.save.core.utils.ProcessTimeoutException +import com.saveourtool.save.core.utils.calculatePathToSarifFile import com.saveourtool.save.core.utils.singleIsInstance import com.saveourtool.sarifutils.cli.adapter.SarifFixAdapter @@ -186,10 +187,15 @@ class FixPlugin( testsPaths: List, testCopyToExpectedFilesMap: List, ): List { + val sarif = calculatePathToSarifFile( + sarifFileName = fixPluginConfig.actualFixSarifFileName!!, + // Since we have one .sarif file for all tests, just take the first of them as anchor for calculation of paths + anchorTestFilePath = testsPaths.first() + ) // In this case fixes weren't performed by tool into the test files directly, // instead, there was created sarif file with list of fixes, which we will apply ourselves val fixedFiles = SarifFixAdapter( - sarifFile = fixPluginConfig.actualFixSarifFileName!!.toPath(), + sarifFile = sarif, targetFiles = testsPaths ).process() diff --git a/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPluginConfig.kt b/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPluginConfig.kt index 3628943fa..bb2fb3837 100644 --- a/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPluginConfig.kt +++ b/save-plugins/fix-plugin/src/commonMain/kotlin/com/saveourtool/save/plugins/fix/FixPluginConfig.kt @@ -82,18 +82,8 @@ data class FixPluginConfig( resourceNameExpectedSuffix = resourceNameExpected, ignoreLines = ignoreLines, actualFixFormat = actualFixFormat ?: ActualFixFormat.IN_PLACE, - actualFixSarifFileName = calculateActualFixSarifFilePath(), + actualFixSarifFileName = (actualFixSarifFileName ?: "save-fixes.sarif"), ).also { it.configLocation = this.configLocation } - - // we require from sarif file to be located at the same level as corresponding save.toml - private fun calculateActualFixSarifFilePath(): String? = if (actualFixFormat == ActualFixFormat.SARIF) { - ( - configLocation.parent!! / - (actualFixSarifFileName ?: "save-fixes.sarif").toPath() - ).toString() - } else { - null - } } diff --git a/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifFileUtils.kt b/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifFileUtils.kt deleted file mode 100644 index 84d4024dc..000000000 --- a/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifFileUtils.kt +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Utility methods to work with SARIF files. - */ - -package com.saveourtool.save.plugin.warn.sarif - -import com.saveourtool.save.core.files.parents - -import okio.FileSystem -import okio.Path - -/** - * @return string with trimmed `file://` or `file:///` - */ -fun String.dropFileProtocol() = substringAfter("file://") - .let { - // It is a valid format for Windows paths to look like `file:///C:/stuff` - if (it[0] == '/' && it[2] == ':') it.drop(1) else it - } - -/** - * Find a file in any of parent directories and return this directory - * - * @param path path for which ancestors should be checked - * @param fileName a name of the file that will be searched for - * @return a path to one of parent directories or null if no directory contains [fileName] - */ -fun FileSystem.findAncestorDirContainingFile(path: Path, fileName: String): Path? = path.parents().firstOrNull { parent -> - metadata(parent).isDirectory && list(parent).any { - it.name == fileName - } -} - -/** - * Make all paths in [this] collection relative to [root] - * - * @param root a common root for files in [this] - * @return a list of relative paths - */ -fun List.adjustToCommonRoot(root: Path) = map { - it.relativeTo(root).normalized() -} - -/** - * Find the last parent directory containing save.toml. - * - * @param path a path to start the search - * @return one of parent directories - */ -internal fun FileSystem.topmostTestDirectory(path: Path): Path = path.parents().last { parent -> - list(parent).any { it.name == "save.toml" } -} diff --git a/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapter.kt b/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapter.kt index 576555cbf..8b22b3e5a 100644 --- a/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapter.kt +++ b/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapter.kt @@ -4,6 +4,7 @@ package com.saveourtool.save.plugin.warn.sarif +import com.saveourtool.save.core.utils.dropFileProtocol import com.saveourtool.save.core.utils.isCurrentOsWindows import com.saveourtool.save.plugin.warn.utils.Warning diff --git a/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/utils/WarningsExtraction.kt b/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/utils/WarningsExtraction.kt index 8917d317e..3130f3a76 100644 --- a/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/utils/WarningsExtraction.kt +++ b/save-plugins/warn-plugin/src/commonMain/kotlin/com/saveourtool/save/plugin/warn/utils/WarningsExtraction.kt @@ -8,11 +8,11 @@ package com.saveourtool.save.plugin.warn.utils import com.saveourtool.save.core.files.readFile import com.saveourtool.save.core.plugin.GeneralConfig import com.saveourtool.save.core.plugin.PluginException +import com.saveourtool.save.core.utils.adjustToCommonRoot +import com.saveourtool.save.core.utils.calculatePathToSarifFile +import com.saveourtool.save.core.utils.topmostTestDirectory import com.saveourtool.save.plugin.warn.WarnPluginConfig -import com.saveourtool.save.plugin.warn.sarif.adjustToCommonRoot -import com.saveourtool.save.plugin.warn.sarif.findAncestorDirContainingFile import com.saveourtool.save.plugin.warn.sarif.toWarnings -import com.saveourtool.save.plugin.warn.sarif.topmostTestDirectory import io.github.detekt.sarif4k.SarifSchema210 import okio.FileSystem @@ -114,11 +114,10 @@ internal fun collectWarningsFromSarif( // Since we have one .sarif file for all tests, just take the first of them as anchor for calculation of paths val anchorTestFilePath = originalPaths.first() - val sarif = fs.findAncestorDirContainingFile(anchorTestFilePath, sarifFileName)?.let { it / sarifFileName } - ?: throw PluginException( - "Could not find SARIF file with expected warnings for file $anchorTestFilePath. " + - "Please check if correct `expectedWarningsFormat` is set and if the file is present and called `$sarifFileName`." - ) + val sarif = calculatePathToSarifFile( + sarifFileName = sarifFileName, + anchorTestFilePath = anchorTestFilePath + ) val topmostTestDirectory = fs.topmostTestDirectory(anchorTestFilePath) return Json.decodeFromString( fs.readFile(sarif) diff --git a/save-plugins/warn-plugin/src/commonNonJsTest/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapterTest.kt b/save-plugins/warn-plugin/src/commonNonJsTest/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapterTest.kt index 22b923d63..5da60063f 100644 --- a/save-plugins/warn-plugin/src/commonNonJsTest/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapterTest.kt +++ b/save-plugins/warn-plugin/src/commonNonJsTest/kotlin/com/saveourtool/save/plugin/warn/sarif/SarifWarningAdapterTest.kt @@ -2,6 +2,7 @@ package com.saveourtool.save.plugin.warn.sarif import com.saveourtool.save.core.files.getWorkingDirectory import com.saveourtool.save.core.logging.logInfo +import com.saveourtool.save.core.utils.adjustToCommonRoot import com.saveourtool.save.plugin.warn.utils.Warning import io.github.detekt.sarif4k.ArtifactLocation