KT-45777: Clean up build cache tests for incremental compilation
- Clean up BuildCacheRelocationIT.testKotlinIncrementalCompilation - Delete BuildCacheIT.testKotlinCompileIncrementalBuildWithoutRelocation as it is already covered by the test in BuildCacheRelocationIT - Add BuildCacheRelocationIT.testKotlinIncrementalCompilation_withClasspathSnapshot - Updated BuildCacheRelocationIT.testKotlinIncrementalCompilation_withClasspathSnapshot
This commit is contained in:
committed by
nataliya.valtman
parent
200cc9f75f
commit
d018031cbf
@@ -16,6 +16,7 @@ enum class BuildAttributeKind : Serializable {
|
||||
}
|
||||
|
||||
enum class BuildAttribute(val kind: BuildAttributeKind, val readableString: String) : Serializable {
|
||||
CACHE_DIRECTORY_NOT_POPULATED(BuildAttributeKind.REBUILD_REASON, "Cache directory not populated"),
|
||||
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON, "Build history file not found"),
|
||||
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON, "ABI snapshot not found"),
|
||||
CLASSPATH_SNAPSHOT_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Classpath snapshot not found"),
|
||||
|
||||
@@ -288,13 +288,13 @@ data class LookupSymbol(val name: String, val scope: String) : Comparable<Lookup
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper of a [LookupMap] which tracks changes to the map after the initialization of this [TrackedLookupMap] instance, (unless
|
||||
* Wrapper of a [LookupMap] which tracks changes to the map after the initialization of this [TrackedLookupMap] instance (unless
|
||||
* [trackChanges] is set to `false`).
|
||||
*/
|
||||
private class TrackedLookupMap(private val lookupMap: LookupMap, private val trackChanges: Boolean) {
|
||||
|
||||
// Note that there may be multiple operations on the same key, and the following sets contain the *latest* differences with the original
|
||||
// set of keys in the map. For example, if a key is added then removed, or vice versa, it will not be present in either set.
|
||||
// Note that there may be multiple operations on the same key, and the following sets contain the *aggregated* differences with the
|
||||
// original set of keys in the map. For example, if a key is added then removed, or vice versa, it will not be present in either set.
|
||||
val addedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
||||
val removedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
||||
|
||||
|
||||
+9
-1
@@ -129,7 +129,15 @@ abstract class IncrementalCompilerRunner<
|
||||
else -> providedChangedFiles
|
||||
}
|
||||
|
||||
val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot)
|
||||
// Check whether the cache directory is populated (note that it may be deleted upon a Gradle build cache hit if the directory is
|
||||
// marked as @LocalState in the Gradle task).
|
||||
val cacheDirectoryNotPopulated = cacheDirectory.walk().none { it.isFile }
|
||||
|
||||
val compilationMode = if (cacheDirectoryNotPopulated) {
|
||||
CompilationMode.Rebuild(BuildAttribute.CACHE_DIRECTORY_NOT_POPULATED)
|
||||
} else {
|
||||
sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot)
|
||||
}
|
||||
|
||||
val exitCode = when (compilationMode) {
|
||||
is CompilationMode.Incremental -> {
|
||||
|
||||
+1
-1
@@ -256,7 +256,7 @@ class IncrementalJvmCompilerRunner(
|
||||
is NotAvailableDueToMissingClasspathSnapshot -> ChangesEither.Unknown(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND)
|
||||
is NotAvailableForNonIncrementalRun -> ChangesEither.Unknown(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
|
||||
is ClasspathSnapshotDisabled -> reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) {
|
||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild(BuildAttribute.IC_IS_NOT_ENABLED)
|
||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY)
|
||||
reporter.reportVerbose { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||
val scopes = caches.lookupCache.lookupSymbols.map { it.scope.ifBlank { it.name } }.distinct()
|
||||
|
||||
|
||||
+1
-2
@@ -1030,8 +1030,7 @@ fun getSomething() = 10
|
||||
.filter { it.contains("[KOTLIN] compile iteration:") }
|
||||
.drop(1)
|
||||
.joinToString(separator = "/n")
|
||||
val actualSources = getCompiledKotlinSources(filteredOutput).projectRelativePaths(project)
|
||||
assertSameFiles(project.relativize(androidModuleKt), actualSources, "Compiled Kotlin files differ:\n ")
|
||||
assertCompiledKotlinSources(project.relativize(androidModuleKt), output = filteredOutput)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-47
@@ -11,26 +11,19 @@ import org.gradle.api.logging.configuration.WarningMode
|
||||
import org.gradle.tooling.GradleConnector
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM
|
||||
import org.jetbrains.kotlin.gradle.model.ModelContainer
|
||||
import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
||||
import org.jetbrains.kotlin.gradle.testbase.applyAndroidTestFixes
|
||||
import org.jetbrains.kotlin.gradle.testbase.enableCacheRedirector
|
||||
import org.jetbrains.kotlin.gradle.testbase.extractJavaCompiledSources
|
||||
import org.jetbrains.kotlin.gradle.testbase.extractKotlinCompiledSources
|
||||
import org.jetbrains.kotlin.gradle.testbase.prettyPrintXml
|
||||
import org.jetbrains.kotlin.gradle.testbase.readAndCleanupTestResults
|
||||
import org.jetbrains.kotlin.gradle.testbase.addPluginManagementToSettings
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.test.RunnerWithMuteInDatabase
|
||||
import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
@@ -386,14 +379,7 @@ abstract class BaseGradleIT {
|
||||
}
|
||||
}
|
||||
|
||||
class CompiledProject(val project: Project, val output: String, val resultCode: Int) {
|
||||
fun getCompiledKotlinSources(output: String) =
|
||||
extractKotlinCompiledSources(project.projectDir.toPath(), output).map { sourcePath -> sourcePath.toFile() }
|
||||
|
||||
val compiledJavaSources: Iterable<File> by lazy {
|
||||
extractJavaCompiledSources(output).map { sourcePath -> sourcePath.toFile() }
|
||||
}
|
||||
}
|
||||
class CompiledProject(val project: Project, val output: String, val resultCode: Int)
|
||||
|
||||
// Basically the same as `Project.build`, tells gradle to wait for debug on 5005 port
|
||||
// Faster to type than `project.build("-Dorg.gradle.debug=true")` or `project.build(options = defaultBuildOptions().copy(debug = true))`
|
||||
@@ -593,27 +579,6 @@ abstract class BaseGradleIT {
|
||||
return map { it.canonicalFile.toRelativeString(project.projectDir) }
|
||||
}
|
||||
|
||||
fun CompiledProject.assertSameFiles(expected: Iterable<String>, actual: Iterable<String>, messagePrefix: String): CompiledProject {
|
||||
val expectedSet = expected.map { it.normalizePath() }.toSortedSet().joinToString("\n")
|
||||
val actualSet = actual.map { it.normalizePath() }.toSortedSet().joinToString("\n")
|
||||
Assert.assertEquals(messagePrefix, expectedSet, actualSet)
|
||||
return this
|
||||
}
|
||||
|
||||
fun CompiledProject.assertContainFiles(
|
||||
expected: Iterable<String>,
|
||||
actual: Iterable<String>,
|
||||
messagePrefix: String = ""
|
||||
): CompiledProject {
|
||||
val expectedNormalized = expected.map(::normalizePath).toSortedSet()
|
||||
val actualNormalized = actual.map(::normalizePath).toSortedSet()
|
||||
assertTrue(
|
||||
actualNormalized.containsAll(expectedNormalized),
|
||||
messagePrefix + "expected files: ${expectedNormalized.joinToString()}\n !in actual files: ${actualNormalized.joinToString()}"
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun CompiledProject.findTasksByPattern(pattern: String): Set<String> {
|
||||
return "task '($pattern)'".toRegex().findAll(output).mapTo(HashSet()) { it.groupValues[1] }
|
||||
}
|
||||
@@ -778,13 +743,14 @@ Finished executing task ':$taskName'|
|
||||
output: String = this.output,
|
||||
suffix: String = ""
|
||||
): CompiledProject {
|
||||
val messagePrefix = "Compiled Kotlin files differ${suffix}:\n "
|
||||
val actualSources = getCompiledKotlinSources(output).projectRelativePaths(this.project)
|
||||
return if (weakTesting) {
|
||||
assertContainFiles(expectedSourcesRelativePaths, actualSources, messagePrefix)
|
||||
val messagePrefix = "Compiled Kotlin files differ${suffix}:\n"
|
||||
val actualSources = extractCompiledKotlinFiles(output)
|
||||
if (weakTesting) {
|
||||
assertContainsFiles(expectedSourcesRelativePaths.toPaths(), actualSources, messagePrefix)
|
||||
} else {
|
||||
assertSameFiles(expectedSourcesRelativePaths, actualSources, messagePrefix)
|
||||
assertSameFiles(expectedSourcesRelativePaths.toPaths(), actualSources, messagePrefix)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun CompiledProject.assertCompiledKotlinFiles(expectedFiles: Iterable<File>): CompiledProject =
|
||||
@@ -799,11 +765,15 @@ Finished executing task ':$taskName'|
|
||||
fun CompiledProject.assertCompiledJavaSources(
|
||||
sources: Iterable<String>,
|
||||
weakTesting: Boolean = false
|
||||
): CompiledProject =
|
||||
): CompiledProject {
|
||||
val messagePrefix = "Compiled Java files differ:\n"
|
||||
val actualSources = extractCompiledJavaFiles(project.projectDir, output)
|
||||
if (weakTesting)
|
||||
assertContainFiles(sources, compiledJavaSources.projectRelativePaths(this.project), "Compiled Java files differ:\n ")
|
||||
assertContainsFiles(sources.toPaths(), actualSources, messagePrefix)
|
||||
else
|
||||
assertSameFiles(sources, compiledJavaSources.projectRelativePaths(this.project), "Compiled Java files differ:\n ")
|
||||
assertSameFiles(sources.toPaths(), actualSources, messagePrefix)
|
||||
return this
|
||||
}
|
||||
|
||||
fun Project.resourcesDir(subproject: String? = null, sourceSet: String = "main"): String =
|
||||
(subproject?.plus("/") ?: "") + "build/resources/$sourceSet/"
|
||||
@@ -904,7 +874,7 @@ Finished executing task ':$taskName'|
|
||||
options.incrementalJsKlib?.let { add("-Pkotlin.incremental.js.klib=$it") }
|
||||
options.jsIrBackend?.let { add("-Pkotlin.js.useIrBackend=$it") }
|
||||
options.usePreciseJavaTracking?.let { add("-Pkotlin.incremental.usePreciseJavaTracking=$it") }
|
||||
options.useClasspathSnapshot?.let { add("-Pkotlin.incremental.useClasspathSnapshot=$it") }
|
||||
options.useClasspathSnapshot?.let { add("-P${COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM.property}=$it") }
|
||||
options.androidGradlePluginVersion?.let { add("-Pandroid_tools_version=$it") }
|
||||
if (options.debug) {
|
||||
add("-Dorg.gradle.debug=true")
|
||||
|
||||
-27
@@ -103,33 +103,6 @@ class BuildCacheIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Incremental compilation works with cache")
|
||||
@GradleTest
|
||||
fun testKotlinCompileIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) {
|
||||
project("buildCacheSimple", gradleVersion) {
|
||||
enableLocalBuildCache(localBuildCacheDir)
|
||||
|
||||
build("assemble") {
|
||||
assertTasksPackedToCache(":compileKotlin")
|
||||
}
|
||||
|
||||
build("clean", "assemble") {
|
||||
assertTasksFromCache(":compileKotlin")
|
||||
}
|
||||
|
||||
val fooKtSourceFile = kotlinSourcesDir().resolve("foo.kt")
|
||||
fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") }
|
||||
build("assemble") {
|
||||
assertIncrementalCompilation(modifiedFiles = setOf(fooKtSourceFile))
|
||||
}
|
||||
|
||||
fooKtSourceFile.modify { it.replace("String = \"abc\"", "Int = 1") }
|
||||
build("clean", "assemble") {
|
||||
assertTasksFromCache(":compileKotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Debug log level should not break build cache")
|
||||
@GradleTest
|
||||
fun testDebugLogLevelCaching(gradleVersion: GradleVersion) {
|
||||
|
||||
+23
-20
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.configuration.WarningMode
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import kotlin.io.path.createDirectory
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
@DisplayName("Build cache relocation")
|
||||
@SimpleGradlePluginTests
|
||||
@@ -260,20 +262,6 @@ class BuildCacheRelocationIT : KGPBaseTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@DisplayName("Incremental compilation build cache does not break relocated cache")
|
||||
@GradleTest
|
||||
fun testKotlinCompileCachingIncrementalBuildWithRelocation(gradleVersion: GradleVersion) {
|
||||
val firstProject = project("buildCacheSimple", gradleVersion) {
|
||||
enableLocalBuildCache(localBuildCacheDir)
|
||||
}
|
||||
|
||||
val secondProject = project("buildCacheSimple", gradleVersion) {
|
||||
enableLocalBuildCache(localBuildCacheDir)
|
||||
}
|
||||
|
||||
checkKotlinCompileCachingIncrementalBuild(firstProject, secondProject)
|
||||
}
|
||||
|
||||
@DisplayName("Kapt incremental compilation works with cache")
|
||||
@GradleTest
|
||||
fun testKaptCachingIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) {
|
||||
@@ -332,10 +320,22 @@ class BuildCacheRelocationIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkKotlinCompileCachingIncrementalBuild(
|
||||
firstProject: TestProject,
|
||||
secondProject: TestProject
|
||||
) {
|
||||
@DisplayName("Kotlin incremental compilation should work correctly")
|
||||
@GradleTest
|
||||
fun testKotlinIncrementalCompilation(gradleVersion: GradleVersion) {
|
||||
checkKotlinIncrementalCompilation(gradleVersion)
|
||||
}
|
||||
|
||||
@DisplayName("Kotlin incremental compilation with `kotlin.incremental.useClasspathSnapshot` feature should work correctly")
|
||||
@GradleTest
|
||||
fun testKotlinIncrementalCompilation_withClasspathSnapshot(gradleVersion: GradleVersion) {
|
||||
checkKotlinIncrementalCompilation(gradleVersion, useClasspathSnapshot = true)
|
||||
}
|
||||
|
||||
private fun checkKotlinIncrementalCompilation(gradleVersion: GradleVersion, useClasspathSnapshot: Boolean? = null) {
|
||||
val buildOptions = defaultBuildOptions.copy(useClasspathSnapshot = useClasspathSnapshot)
|
||||
val (firstProject, secondProject) = prepareTestProjects("buildCacheSimple", gradleVersion, buildOptions)
|
||||
|
||||
// First build, should be stored into the build cache:
|
||||
firstProject.build("assemble") {
|
||||
assertTasksPackedToCache(":compileKotlin")
|
||||
@@ -348,9 +348,12 @@ class BuildCacheRelocationIT : KGPBaseTest() {
|
||||
|
||||
// Change the return type of foo() from Int to String in foo.kt, and check that fooUsage.kt is recompiled as well:
|
||||
val fooKtSourceFile = secondProject.kotlinSourcesDir().resolve("foo.kt")
|
||||
val fooUsageKtSourceFile = secondProject.kotlinSourcesDir().resolve("fooUsage.kt")
|
||||
fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") }
|
||||
secondProject.build("assemble") {
|
||||
assertIncrementalCompilation(modifiedFiles = setOf(fooKtSourceFile))
|
||||
secondProject.build("assemble", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) {
|
||||
assertIncrementalCompilation(
|
||||
listOf(fooKtSourceFile, fooUsageKtSourceFile).relativizeTo(secondProject.projectPath).map { it.pathString }
|
||||
)
|
||||
}
|
||||
|
||||
// Revert the change to the return type of foo(), and check if we get a cache hit
|
||||
|
||||
+4
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.testbase
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.configuration.WarningMode
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM
|
||||
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
||||
@@ -21,6 +22,7 @@ data class BuildOptions(
|
||||
val configurationCacheProblems: BaseGradleIT.ConfigurationCacheProblems = BaseGradleIT.ConfigurationCacheProblems.FAIL,
|
||||
val parallel: Boolean = true,
|
||||
val incremental: Boolean? = null,
|
||||
val useClasspathSnapshot: Boolean? = null,
|
||||
val maxWorkers: Int = (Runtime.getRuntime().availableProcessors() / 4 - 1).coerceAtLeast(2),
|
||||
val fileSystemWatchEnabled: Boolean = false,
|
||||
val buildCacheEnabled: Boolean = false,
|
||||
@@ -81,6 +83,8 @@ data class BuildOptions(
|
||||
arguments.add("-Pkotlin.incremental=$incremental")
|
||||
}
|
||||
|
||||
useClasspathSnapshot?.let { arguments.add("-P${COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM.property}=$it") }
|
||||
|
||||
if (gradleVersion >= GradleVersion.version("6.5")) {
|
||||
if (fileSystemWatchEnabled) {
|
||||
arguments.add("--watch-fs")
|
||||
|
||||
+42
-29
@@ -5,14 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.testbase
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.relativeTo
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.intellij.lang.annotations.Language
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
private val kotlinSrcRegex by lazy { Regex("\\[KOTLIN\\] compile iteration: ([^\\r\\n]*)") }
|
||||
private val kotlinSrcRegex by lazy { Regex("\\[KOTLIN] compile iteration: ([^\\r\\n]*)") }
|
||||
|
||||
private val javaSrcRegex by lazy { Regex("\\[DEBUG] \\[[^]]*JavaCompiler] Compiler arguments: ([^\\r\\n]*)") }
|
||||
|
||||
@Language("RegExp")
|
||||
private fun taskOutputRegex(
|
||||
@@ -42,45 +44,56 @@ fun BuildResult.getOutputForTask(taskName: String): String = taskOutputRegex(tas
|
||||
?: error("Could not find output for task $taskName")
|
||||
|
||||
/**
|
||||
* Extracts list of compiled .kt files from task output
|
||||
* Extracts the list of compiled .kt files from the build output.
|
||||
*
|
||||
* Note: log level of output should be set to [LogLevel.DEBUG]
|
||||
* The returned paths are relative to the project directory.
|
||||
*
|
||||
* Note: Log level of output must be set to [LogLevel.DEBUG].
|
||||
*/
|
||||
fun extractKotlinCompiledSources(projectDirectory: Path, output: String) =
|
||||
kotlinSrcRegex.findAll(output)
|
||||
.asIterable()
|
||||
.flatMap { result ->
|
||||
result.groups[1]!!.value.split(", ")
|
||||
.map { source -> projectDirectory.resolve(source).normalize() }
|
||||
}
|
||||
|
||||
private val javaSrcRegex by lazy { Regex("\\[DEBUG\\] \\[[^\\]]*JavaCompiler\\] Compiler arguments: ([^\\r\\n]*)") }
|
||||
fun extractCompiledKotlinFiles(output: String): List<Path> {
|
||||
return kotlinSrcRegex.findAll(output).asIterable()
|
||||
.flatMap { matchResult -> matchResult.groups[1]!!.value.split(", ") }
|
||||
.toPaths()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts list of compiled .java files from task output
|
||||
* Extracts the list of compiled .java files from the build output.
|
||||
*
|
||||
* Note: log level of output should be set to [LogLevel.DEBUG]
|
||||
* The returned paths are relative to the project directory.
|
||||
*
|
||||
* Note: Log level of output must be set to [LogLevel.DEBUG].
|
||||
*/
|
||||
fun extractJavaCompiledSources(output: String): List<Path> =
|
||||
javaSrcRegex.findAll(output).asIterable().flatMap {
|
||||
it.groups[1]!!.value
|
||||
.split(" ")
|
||||
.filter { source -> source.endsWith(".java", ignoreCase = true) }
|
||||
.map { source -> Paths.get(source).normalize() }
|
||||
}
|
||||
fun extractCompiledJavaFiles(projectDir: File, output: String): List<Path> {
|
||||
return javaSrcRegex.findAll(output).asIterable()
|
||||
.flatMap { matchResult -> matchResult.groups[1]!!.value.split(" ") }
|
||||
.filter { filePath -> filePath.endsWith(".java", ignoreCase = true) }
|
||||
.map { javaFilePath -> projectDir.toPath().relativize(Paths.get(javaFilePath)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts all the .kt files from [expectedSources] and only they are compiled
|
||||
*
|
||||
* Note: log level of output should be set to [LogLevel.DEBUG]
|
||||
*/
|
||||
fun GradleProject.assertCompiledKotlinSources(
|
||||
fun assertCompiledKotlinSources(
|
||||
expectedSources: Iterable<Path>,
|
||||
output: String,
|
||||
errorMessageSuffix: String = ""
|
||||
) {
|
||||
val actualSources = extractKotlinCompiledSources(projectPath, output).map {
|
||||
it.relativeTo(projectPath)
|
||||
}
|
||||
val actualSources = extractCompiledKotlinFiles(output)
|
||||
assertSameFiles(expectedSources, actualSources, "Compiled Kotlin files differ${errorMessageSuffix}:\n")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that compilation was incremental and the set of compiled .kt files exactly match [expectedCompiledKotlinFiles].
|
||||
*
|
||||
* [expectedCompiledKotlinFiles] are relative to the project directory.
|
||||
*
|
||||
* Note: Log level of output must be set to [LogLevel.DEBUG].
|
||||
*/
|
||||
fun BuildResult.assertIncrementalCompilation(expectedCompiledKotlinFiles: Iterable<String>) {
|
||||
assertOutputDoesNotContain("Non-incremental compilation will be performed")
|
||||
|
||||
val actualCompiledKotlinFiles = extractCompiledKotlinFiles(output)
|
||||
assertSameFiles(expectedCompiledKotlinFiles.toPaths(), actualCompiledKotlinFiles, "Compiled Kotlin files differ:\n")
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ fun TestProject.assertSimpleConfigurationCacheScenarioWorks(
|
||||
*/
|
||||
@OptIn(ExperimentalPathApi::class)
|
||||
private fun copyReportToTempDir(htmlReportFile: Path): Path =
|
||||
createTempDir("report").let { tempDir ->
|
||||
createTempDirDeleteOnExit("report").let { tempDir ->
|
||||
htmlReportFile.parent.toFile().copyRecursively(tempDir.toFile())
|
||||
tempDir.resolve(htmlReportFile.name)
|
||||
}
|
||||
|
||||
+22
-6
@@ -10,7 +10,7 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.asserter
|
||||
|
||||
/**
|
||||
* Asserts file under [file] path exists and is a regular file.
|
||||
@@ -189,8 +189,24 @@ fun GradleProject.assertFileDoesNotContain(
|
||||
}
|
||||
}
|
||||
|
||||
fun GradleProject.assertSameFiles(expected: Iterable<Path>, actual: Iterable<Path>, messagePrefix: String) {
|
||||
val expectedSet = expected.map { it.toString().normalizePath() }.toSortedSet().joinToString("\n")
|
||||
val actualSet = actual.map { it.toString().normalizePath() }.toSortedSet().joinToString("\n")
|
||||
assertEquals(expectedSet, actualSet, messagePrefix)
|
||||
}
|
||||
fun assertSameFiles(expected: Iterable<Path>, actual: Iterable<Path>, messagePrefix: String) {
|
||||
val expectedSet = expected.map { it.toString().normalizePath() }.toSet()
|
||||
val actualSet = actual.map { it.toString().normalizePath() }.toSet()
|
||||
asserter.assertTrue(lazyMessage = {
|
||||
messagePrefix +
|
||||
"Actual set does not exactly match expected set.\n" +
|
||||
"Expected set: ${expectedSet.sorted().joinToString(", ")}\n" +
|
||||
"Actual set: ${actualSet.sorted().joinToString(", ")}\n"
|
||||
}, actualSet.size == expectedSet.size && actualSet.containsAll(expectedSet))
|
||||
}
|
||||
|
||||
fun assertContainsFiles(expected: Iterable<Path>, actual: Iterable<Path>, messagePrefix: String) {
|
||||
val expectedSet = expected.map { it.toString().normalizePath() }.toSet()
|
||||
val actualSet = actual.map { it.toString().normalizePath() }.toSet()
|
||||
asserter.assertTrue(lazyMessage = {
|
||||
messagePrefix +
|
||||
"Actual set does not contain all of expected set.\n" +
|
||||
"Expected set: ${expectedSet.sorted().joinToString(", ")}\n" +
|
||||
"Actual set: ${actualSet.sorted().joinToString(", ")}\n"
|
||||
}, actualSet.containsAll(expectedSet))
|
||||
}
|
||||
|
||||
-65
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.gradle.testbase
|
||||
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Asserts Gradle output contains [expectedSubString] string.
|
||||
@@ -127,70 +126,6 @@ fun BuildResult.assertNoBuildWarnings(
|
||||
}
|
||||
}
|
||||
|
||||
fun BuildResult.assertIncrementalCompilation(
|
||||
modifiedFiles: Set<Path> = emptySet(),
|
||||
deletedFiles: Set<Path> = emptySet()
|
||||
) {
|
||||
val incrementalCompilationOptions = output
|
||||
.lineSequence()
|
||||
.filter { it.trim().startsWith("Options for KOTLIN DAEMON: IncrementalCompilationOptions(") }
|
||||
.map {
|
||||
it
|
||||
.removePrefix("Options for KOTLIN DAEMON: IncrementalCompilationOptions(")
|
||||
.removeSuffix(")")
|
||||
}
|
||||
.toList()
|
||||
|
||||
assert(incrementalCompilationOptions.isNotEmpty()) {
|
||||
printBuildOutput()
|
||||
"No incremental compilation options were found in the build"
|
||||
}
|
||||
|
||||
val modifiedFilesPath = modifiedFiles.map { it.toAbsolutePath().toString() }
|
||||
val deletedFilesPath = deletedFiles.map { it.toAbsolutePath().toString() }
|
||||
val hasMatch = incrementalCompilationOptions
|
||||
.firstOrNull {
|
||||
val optionModifiedFiles = it
|
||||
.substringAfter("modifiedFiles=[")
|
||||
.substringBefore("]")
|
||||
.split(",")
|
||||
.filter(String::isNotEmpty)
|
||||
|
||||
val modifiedFilesFound = if (modifiedFilesPath.isEmpty()) {
|
||||
optionModifiedFiles.isEmpty()
|
||||
} else {
|
||||
modifiedFilesPath.subtract(optionModifiedFiles).isEmpty()
|
||||
}
|
||||
|
||||
val optionDeletedFiles = it
|
||||
.substringAfter("deletedFiles=[")
|
||||
.substringBefore("]")
|
||||
.split(",")
|
||||
.filter(String::isNotEmpty)
|
||||
|
||||
val deletedFilesFound = if (deletedFilesPath.isEmpty()) {
|
||||
optionDeletedFiles.isEmpty()
|
||||
} else {
|
||||
deletedFilesPath.subtract(optionDeletedFiles).isEmpty()
|
||||
}
|
||||
|
||||
modifiedFilesFound && deletedFilesFound
|
||||
} != null
|
||||
|
||||
assert(hasMatch) {
|
||||
printBuildOutput()
|
||||
|
||||
"""
|
||||
|Expected incremental compilation options with:
|
||||
|- modified files: ${modifiedFilesPath.joinToString()}
|
||||
|- deleted files: ${deletedFilesPath.joinToString()}
|
||||
|
|
||||
|but none of following compilation options match:
|
||||
|${incrementalCompilationOptions.joinToString(separator = "\n")}
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts compilation is running via Kotlin daemon with given jvm arguments.
|
||||
*/
|
||||
|
||||
+4
-5
@@ -6,10 +6,7 @@
|
||||
package org.jetbrains.kotlin.gradle.testbase
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.FileVisitResult
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.SimpleFileVisitor
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import kotlin.io.path.*
|
||||
import kotlin.streams.asSequence
|
||||
@@ -50,7 +47,7 @@ internal val Path.allJavaSources: List<Path>
|
||||
*
|
||||
* Prefer using JUnit5 `@TempDir` over this method when possible.
|
||||
*/
|
||||
internal fun createTempDir(prefix: String): Path = Files
|
||||
internal fun createTempDirDeleteOnExit(prefix: String): Path = Files
|
||||
.createTempDirectory(prefix)
|
||||
.apply { toFile().deleteOnExit() }
|
||||
|
||||
@@ -108,3 +105,5 @@ internal fun Path.deleteRecursively() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
internal fun Iterable<String>.toPaths(): List<Path> = map { Paths.get(it) }
|
||||
|
||||
+2
-7
@@ -5,10 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.util
|
||||
|
||||
import org.jetbrains.kotlin.gradle.testbase.createTempDirDeleteOnExit
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.relativeTo
|
||||
|
||||
fun File.getFileByName(name: String): File =
|
||||
findFileByName(name) ?: throw AssertionError("Could not find file with name '$name' in $this")
|
||||
@@ -36,10 +34,7 @@ fun File.addNewLine() {
|
||||
modify { "$it\n" }
|
||||
}
|
||||
|
||||
fun createTempDir(prefix: String): File =
|
||||
Files.createTempDirectory(prefix).toFile().apply {
|
||||
deleteOnExit()
|
||||
}
|
||||
fun createTempDir(prefix: String): File = createTempDirDeleteOnExit(prefix).toFile()
|
||||
|
||||
/**
|
||||
* converts back slashes to forward slashes
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// This class is used so that the set of compiled source files in an incremental compilation is a proper subset of the set of compiled
|
||||
// source files in a non-incremental compilation, so that the two cases are not ambiguous when we check the set of compiled source files.
|
||||
class StandAloneClass
|
||||
Reference in New Issue
Block a user