[K/N][tests] Added a bunch of tests on incremental compilation

This commit is contained in:
Igor Chevdar
2023-01-04 22:46:26 +02:00
committed by Space Team
parent e4f30589a4
commit 9fcb4ece64
58 changed files with 956 additions and 9 deletions
@@ -0,0 +1 @@
fun foo() = 42
@@ -0,0 +1 @@
fun bar() = 117
@@ -0,0 +1,7 @@
import kotlin.test.*
@Test
fun runTest() {
assertEquals(42, foo())
assertEquals(117, bar())
}
@@ -0,0 +1,4 @@
import kotlin.test.*
@Test
fun runTest() = assertEquals(42, foo())
@@ -0,0 +1,6 @@
package test1
interface I {
fun baz(): String
fun foo(): Int
}
@@ -0,0 +1,5 @@
package test1
interface I {
fun foo(): Int
}
@@ -0,0 +1,5 @@
package test2
import test1.*
fun bar(x: I) = x.foo()
@@ -0,0 +1,13 @@
import kotlin.test.*
import test1.*
import test2.*
class IImpl : I {
override fun baz() = "zzz"
override fun foo() = 42
}
@Test
fun runTest() {
assertEquals(42, bar(IImpl()))
}
@@ -0,0 +1,12 @@
import kotlin.test.*
import test1.*
import test2.*
class IImpl : I {
override fun foo() = 42
}
@Test
fun runTest() {
assertEquals(42, bar(IImpl()))
}
@@ -0,0 +1,6 @@
package test1
interface I {
fun baz(): String
fun foo(): Int
}
@@ -0,0 +1,5 @@
package test1
interface I {
fun foo(): Int
}
@@ -0,0 +1,5 @@
package test1
interface J : I {
fun bar(): String
}
@@ -0,0 +1,5 @@
package test2
import test1.*
fun bar(x: J) = x.bar()
@@ -0,0 +1,14 @@
import kotlin.test.*
import test1.*
import test2.*
class JImpl : J {
override fun baz() = "qxx"
override fun foo() = 42
override fun bar() = "zzz"
}
@Test
fun runTest() {
assertEquals("zzz", bar(JImpl()))
}
@@ -0,0 +1,13 @@
import kotlin.test.*
import test1.*
import test2.*
class JImpl : J {
override fun foo() = 42
override fun bar() = "zzz"
}
@Test
fun runTest() {
assertEquals("zzz", bar(JImpl()))
}
@@ -0,0 +1,6 @@
package test1
open class A {
open fun baz() = "zzz"
open fun foo() = 117
}
@@ -0,0 +1,5 @@
package test1
open class A {
open fun foo() = 117
}
@@ -0,0 +1,5 @@
package test2
import test1.*
fun bar(a: A) = a.foo()
@@ -0,0 +1,12 @@
import kotlin.test.*
import test1.*
import test2.*
class B : A() {
override fun foo() = 42
}
@Test
fun runTest() {
assertEquals(42, bar(B()))
}
@@ -0,0 +1,6 @@
package test1
open class A {
open fun baz() = "qxx"
open fun foo() = 117
}
@@ -0,0 +1,5 @@
package test1
open class A {
open fun foo() = 117
}
@@ -0,0 +1,5 @@
package test1
open class B : A() {
open fun bar() = "zzz"
}
@@ -0,0 +1,5 @@
package test2
import test1.*
fun bar(b: B) = b.bar()
@@ -0,0 +1,12 @@
import kotlin.test.*
import test1.*
import test2.*
class C : B() {
override fun bar() = "qzz"
}
@Test
fun runTest() {
assertEquals("qzz", bar(C()))
}
@@ -0,0 +1,5 @@
inline fun foo(arr: IntArray, transform: (Int) -> Int): Int {
var sum = 0
for (x in arr) sum += transform(x)
return sum
}
@@ -0,0 +1,8 @@
inline fun bar(arr: IntArray, transform: (Int) -> Int): Int {
var max = Int.MIN_VALUE
for (x in arr) {
val y = transform(x)
if (y > max) max = y
}
return max
}
@@ -0,0 +1,11 @@
import kotlin.test.*
@Test
fun checkFoo() {
assertEquals(42, foo(intArrayOf(1, 2, 3, 4, 5, 6)) { it * 2 })
}
@Test
fun checkBar() {
assertEquals(42, bar(intArrayOf(1, 3, 7)) { it * 6 })
}
@@ -0,0 +1,3 @@
package external
fun foo() = 42
@@ -0,0 +1,3 @@
package external
fun foo2() = 128
@@ -0,0 +1,3 @@
package external
fun foo2() = 117
@@ -0,0 +1,6 @@
import kotlin.test.*
@Test
fun doTest() {
assertEquals(42, bar())
}
@@ -0,0 +1,6 @@
import kotlin.test.*
@Test
fun doTest2() {
assertEquals(128, bar2())
}
@@ -0,0 +1,6 @@
import kotlin.test.*
@Test
fun doTest2() {
assertEquals(117, bar2())
}
@@ -0,0 +1 @@
fun bar() = external.foo()
@@ -0,0 +1 @@
fun bar2() = external.foo2()
@@ -0,0 +1,3 @@
package test
fun foo() = 20
@@ -0,0 +1,3 @@
package test
fun foo() = 21
@@ -0,0 +1,3 @@
package test
fun bar() = 117
@@ -0,0 +1,3 @@
package test
fun baz() = foo() * 2
@@ -0,0 +1,8 @@
import kotlin.test.*
import test.*
@Test
fun runTest() {
assertEquals(117, bar())
assertEquals(40, baz())
}
@@ -0,0 +1,8 @@
import kotlin.test.*
import test.*
@Test
fun runTest() {
assertEquals(117, bar())
assertEquals(42, baz())
}
@@ -0,0 +1,3 @@
package test1
fun foo() = 20
@@ -0,0 +1,3 @@
package test1
fun foo() = 21
@@ -0,0 +1,3 @@
package test2
fun bar() = 117
@@ -0,0 +1,5 @@
package test2
import test1.*
fun baz() = foo() * 2
@@ -0,0 +1,9 @@
import kotlin.test.*
import test1.*
import test2.*
@Test
fun runTest() {
assertEquals(117, bar())
assertEquals(40, baz())
}
@@ -0,0 +1,9 @@
import kotlin.test.*
import test1.*
import test2.*
@Test
fun runTest() {
assertEquals(117, bar())
assertEquals(42, baz())
}
@@ -0,0 +1,3 @@
package tezd
fun foo() = 42
@@ -0,0 +1,3 @@
package test
fun foo() = 42
@@ -0,0 +1,5 @@
import kotlin.test.*
import tezd.*
@Test
fun runTest() = assertEquals(42, foo())
@@ -0,0 +1,5 @@
import kotlin.test.*
import test.*
@Test
fun runTest() = assertEquals(42, foo())
@@ -0,0 +1 @@
fun foo() = 41
@@ -0,0 +1 @@
fun foo() = 42
@@ -0,0 +1,4 @@
import kotlin.test.*
@Test
fun runTest() = assertEquals(41, foo())
@@ -0,0 +1,4 @@
import kotlin.test.*
@Test
fun runTest() = assertEquals(42, foo())
@@ -42,7 +42,7 @@ class CachesAutoBuildTest : AbstractNativeSimpleTest() {
val lib = compileToLibrary(rootDir.resolve("lib"), buildDir)
val main = compileToExecutable(rootDir.resolve("main"), autoCacheFrom = buildDir, emptyList(), lib)
assertTrue(main.executableFile.exists())
assertTrue(main.exists())
assertTrue(autoCacheDir.resolve(cacheFlavor).resolve("lib").exists())
}
@@ -58,7 +58,7 @@ class CachesAutoBuildTest : AbstractNativeSimpleTest() {
externalLib, userLib
)
assertTrue(main.executableFile.exists())
assertTrue(main.exists())
assertTrue(autoCacheDir.resolve(cacheFlavor).resolve("externalLib").exists())
assertFalse(autoCacheDir.resolve(cacheFlavor).resolve("userLib").exists())
}
@@ -75,12 +75,13 @@ class CachesAutoBuildTest : AbstractNativeSimpleTest() {
assertTrue(cacheDir.resolve("lib-${if (makePerFileCache) "per-file-cache" else "cache"}").exists())
val main = compileToExecutable(rootDir.resolve("main"), autoCacheFrom = buildDir, listOf(cacheDir), lib)
assertTrue(main.executableFile.exists())
assertTrue(main.exists())
assertFalse(autoCacheDir.resolve(cacheFlavor).resolve("lib").exists())
}
private fun compileToExecutable(sourcesDir: File, autoCacheFrom: File, cacheDirectories: List<File>, vararg dependencies: KLIB) =
compileToExecutable(
private fun compileToExecutable(sourcesDir: File, autoCacheFrom: File, cacheDirectories: List<File>, vararg dependencies: KLIB): File {
autoCacheDir.mkdirs()
return compileToExecutable(
sourcesDir,
freeCompilerArgs = TestCompilerArgs(
listOf(
@@ -89,7 +90,8 @@ class CachesAutoBuildTest : AbstractNativeSimpleTest() {
) + cacheDirectories.map { "-Xcache-directory=${it.absolutePath}" }
),
*dependencies
).assertSuccess().resultingArtifact
).executableFile
}
private val autoCacheDir: File get() = buildDir.resolve("__auto_cache__")
private val cacheFlavor: String
@@ -0,0 +1,542 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.konan.blackboxtest
import com.intellij.testFramework.TestDataPath
import org.jetbrains.kotlin.konan.blackboxtest.CachesAutoBuildTest.Companion.TEST_SUITE_PATH
import org.jetbrains.kotlin.konan.blackboxtest.support.EnforcedHostTarget
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UsePartialLinkage
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheMode
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.OptimizationMode
import org.jetbrains.kotlin.test.TestMetadata
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertFalse
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertNotEquals
import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import java.io.File
@Tag("caches")
@EnforcedHostTarget
@TestMetadata(TEST_SUITE_PATH)
@TestDataPath("\$PROJECT_ROOT")
@UsePartialLinkage(UsePartialLinkage.Mode.DISABLED)
class IncrementalCompilationTest : AbstractNativeSimpleTest() {
@BeforeEach
fun assumeCachesAreEnabled() {
Assumptions.assumeFalse(testRunSettings.get<CacheMode>() == CacheMode.WithoutCache)
}
@Test
@TestMetadata("simple")
fun testSimple() = withRootDir(File("$TEST_SUITE_PATH/simple")) {
val lib = compileLibrary("lib") { "lib/lib.kt" copyTo "lib.kt" }
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val libKtCacheDir = getLibraryFileCache("lib", "lib/lib.kt", "")
assertTrue(libKtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s cache won't be recompiled if nothing has changed.
val modified = libKtCacheDir.lastModified()
compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
assertTrue(libKtCacheDir.exists())
assertEquals(modified, libKtCacheDir.lastModified())
}
@Test
@TestMetadata("modifiedFile")
fun testModifiedFile() = withRootDir(File("$TEST_SUITE_PATH/simple")) {
val lib = compileLibrary("lib") { "lib/lib.kt" copyTo "lib.kt" }
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val libKtCacheDir = getLibraryFileCache("lib", "lib/lib.kt", "")
assertTrue(libKtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s cache will be recompiled after changing the file.
val modified = libKtCacheDir.lastModified()
val lib1 = compileLibrary("lib") { "lib/lib.1.kt" copyTo "lib.kt" }
val main1 = compileToExecutable("main", lib1) { "main/main.1.kt" copyTo "main.kt" }
assertTrue(libKtCacheDir.exists())
assertNotEquals(modified, libKtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("addedFile")
fun testAddedFile() = withRootDir(File("$TEST_SUITE_PATH/addDeleteFile")) {
val lib = compileLibrary("lib") { "lib/lib.file1.kt" copyTo "lib.file1.kt" }
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val libFile1KtCacheDir = getLibraryFileCache("lib", "lib/lib.file1.kt", "")
assertTrue(libFile1KtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s file1 cache won't change after adding an independent file.
val modified = libFile1KtCacheDir.lastModified()
val lib1 = compileLibrary("lib") {
"lib/lib.file1.kt" copyTo "lib.file1.kt"
"lib/lib.file2.kt" copyTo "lib.file2.kt"
}
val main1 = compileToExecutable("main", lib1) { "main/main.1.kt" copyTo "main.kt" }
assertTrue(libFile1KtCacheDir.exists())
assertEquals(modified, libFile1KtCacheDir.lastModified())
val libFile2KtCacheDir = getLibraryFileCache("lib", "lib/lib.file2.kt", "")
assertTrue(libFile2KtCacheDir.exists())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("removedFile")
fun testRemovedFile() = withRootDir(File("$TEST_SUITE_PATH/addDeleteFile")) {
val lib = compileLibrary("lib") {
"lib/lib.file1.kt" copyTo "lib.file1.kt"
"lib/lib.file2.kt" copyTo "lib.file2.kt"
}
val main = compileToExecutable("main", lib) { "main/main.1.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val libFile1KtCacheDir = getLibraryFileCache("lib", "lib/lib.file1.kt", "")
assertTrue(libFile1KtCacheDir.exists())
val libFile2KtCacheDir = getLibraryFileCache("lib", "lib/lib.file2.kt", "")
assertTrue(libFile2KtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s file1 cache won't change after removing an independent file.
val modified = libFile1KtCacheDir.lastModified()
val lib1 = compileLibrary("lib") { "lib/lib.file1.kt" copyTo "lib.file1.kt" }
val main1 = compileToExecutable("main", lib1) { "main/main.kt" copyTo "main.kt" }
assertTrue(libFile1KtCacheDir.exists())
assertEquals(modified, libFile1KtCacheDir.lastModified())
// Check, <lib>'s file2 cache has been removed.
assertFalse(libFile2KtCacheDir.exists())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("renamedFile")
fun testRenamedFile() = withRootDir(File("$TEST_SUITE_PATH/renameFileOrPackage")) {
val lib = compileLibrary("lib") { "lib/lib.kt" copyTo "lib.kt" }
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val libKtCacheDir = getLibraryFileCache("lib", "lib/lib.kt", "test")
assertTrue(libKtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s file cache is moved to other directory.
val lib1 = compileLibrary("lib") { "lib/lib.kt" copyTo "lib.changed.kt" }
val main1 = compileToExecutable("main", lib1) { "main/main.kt" copyTo "main.kt" }
assertFalse(libKtCacheDir.exists())
val changedLibKtCacheDir = getLibraryFileCache("lib", "lib/lib.changed.kt", "test")
assertTrue(changedLibKtCacheDir.exists())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("renamedPackage")
fun testRenamedPackage() = withRootDir(File("$TEST_SUITE_PATH/renameFileOrPackage")) {
val lib = compileLibrary("lib") { "lib/lib.kt" copyTo "lib.kt" }
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val libKtCacheDir = getLibraryFileCache("lib", "lib/lib.kt", "test")
assertTrue(libKtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s file cache is moved to other directory.
val lib1 = compileLibrary("lib") { "lib/lib.1.kt" copyTo "lib.kt" }
val main1 = compileToExecutable("main", lib1) { "main/main.1.kt" copyTo "main.kt" }
assertFalse(libKtCacheDir.exists())
val changedLibKtCacheDir = getLibraryFileCache("lib", "lib/lib.kt", "tezd")
assertTrue(changedLibKtCacheDir.exists())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("changedFileIndex")
fun testChangedFileIndex() = withRootDir(File("$TEST_SUITE_PATH/changeFileIndex")) {
val lib = compileLibrary("lib") {
"lib/lib.file1.kt" copyTo "libB.kt"
"lib/lib.file2.kt" copyTo "libC.kt"
}
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
assertTrue(main.executableFile.exists())
val libBKtCacheDir = getLibraryFileCache("lib", "lib/libB.kt", "")
assertTrue(libBKtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
val modified = libBKtCacheDir.lastModified()
val lib1 = compileLibrary("lib") {
"lib/lib.file1.kt" copyTo "libB.kt"
"lib/lib.file2.kt" copyTo "libA.kt"
}
val main1 = compileToExecutable("main", lib1) { "main/main.kt" copyTo "main.kt" }
assertTrue(libBKtCacheDir.exists())
assertEquals(modified, libBKtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("changedExternalDependencyVersion")
fun testChangedExternalDependencyVersion() = withRootDir(File("$TEST_SUITE_PATH/externalDependency")) {
val externalLib = compileLibrary("externalLib") {
libraryVersion = "1.0"
outputDir = "external"
"externalLib/file1.kt" copyTo "file1.kt"
}
val userLib = compileLibrary("userLib", externalLib) { "userLib/file1.kt" copyTo "file1.kt" }
val main = compileToExecutable("main", externalLib, userLib) { "main/main.kt" copyTo "main.kt" }
assertTrue(main.executableFile.exists())
val libFile1KtCacheDir = getLibraryFileCache("userLib", "userLib/file1.kt", "")
assertTrue(libFile1KtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
val modified = libFile1KtCacheDir.lastModified()
val externalLib1 = compileLibrary("externalLib") {
libraryVersion = "1.1"
outputDir = "external"
"externalLib/file1.kt" copyTo "file1.kt"
}
val userLib1 = compileLibrary("userLib", externalLib1) { "userLib/file1.kt" copyTo "file1.kt" }
val main1 = compileToExecutable("main", externalLib1, userLib1) { "main/main.kt" copyTo "main.kt" }
assertTrue(libFile1KtCacheDir.exists())
assertNotEquals(modified, libFile1KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("changedExternalDependency")
fun testChangedExternalDependency() = withRootDir(File("$TEST_SUITE_PATH/externalDependency")) {
val externalLib = compileLibrary("externalLib") {
outputDir = "external"
"externalLib/file1.kt" copyTo "file1.kt"
"externalLib/file2.kt" copyTo "file2.kt"
}
val userLib = compileLibrary("userLib", externalLib) {
"userLib/file1.kt" copyTo "file1.kt"
"userLib/file2.kt" copyTo "file2.kt"
}
val main = compileToExecutable("main", externalLib, userLib) {
"main/main.kt" copyTo "main.kt"
"main/main2.kt" copyTo "main2.kt"
}
assertTrue(main.executableFile.exists())
val libFile1KtCacheDir = getLibraryFileCache("userLib", "userLib/file1.kt", "")
val libFile2KtCacheDir = getLibraryFileCache("userLib", "userLib/file2.kt", "")
assertTrue(libFile1KtCacheDir.exists())
assertTrue(libFile2KtCacheDir.exists())
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, both file caches will be recompiled after changing only one file in the external library.
val modified1 = libFile1KtCacheDir.lastModified()
val modified2 = libFile2KtCacheDir.lastModified()
val externalLib1 = compileLibrary("externalLib") {
outputDir = "external"
"externalLib/file1.kt" copyTo "file1.kt"
"externalLib/file2.1.kt" copyTo "file2.kt"
}
val main1 = compileToExecutable("main", externalLib1, userLib) {
"main/main.kt" copyTo "main.kt"
"main/main2.1.kt" copyTo "main2.kt"
}
assertTrue(libFile1KtCacheDir.exists())
assertNotEquals(modified1, libFile1KtCacheDir.lastModified())
assertTrue(libFile2KtCacheDir.exists())
assertNotEquals(modified2, libFile2KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("fileDependencies1")
fun testFileDependencies1() = withRootDir(File("$TEST_SUITE_PATH/fileDependencies1")) {
val lib = compileLibrary("lib") {
"lib/lib.file1.kt" copyTo "file1.kt"
"lib/lib.file2.kt" copyTo "file2.kt"
"lib/lib.file3.kt" copyTo "file3.kt"
}
val main = compileToExecutable("main", lib) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val file1KtCacheDir = getLibraryFileCache("lib", "lib/file1.kt", "test")
val file2KtCacheDir = getLibraryFileCache("lib", "lib/file2.kt", "test")
val file3KtCacheDir = getLibraryFileCache("lib", "lib/file3.kt", "test")
assertTrue(file1KtCacheDir.exists())
assertTrue(file2KtCacheDir.exists())
assertTrue(file3KtCacheDir.exists())
val modified1 = file1KtCacheDir.lastModified()
val modified2 = file2KtCacheDir.lastModified()
val modified3 = file3KtCacheDir.lastModified()
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib>'s file1 and file3 caches will be recompiled after changing file1.
val lib1 = compileLibrary("lib") {
"lib/lib.file1.1.kt" copyTo "file1.kt"
"lib/lib.file2.kt" copyTo "file2.kt"
"lib/lib.file3.kt" copyTo "file3.kt"
}
val main1 = compileToExecutable("main", lib1) { "main/main.1.kt" copyTo "main.kt" }
assertTrue(main1.executableFile.exists())
assertTrue(file1KtCacheDir.exists())
assertTrue(file2KtCacheDir.exists())
assertTrue(file3KtCacheDir.exists())
assertNotEquals(modified1, file1KtCacheDir.lastModified())
assertEquals(modified2, file2KtCacheDir.lastModified())
assertNotEquals(modified3, file3KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("fileDependencies2")
fun testFileDependencies2() = withRootDir(File("$TEST_SUITE_PATH/fileDependencies2")) {
val lib1 = compileLibrary("lib1") { "lib1/lib1.kt" copyTo "lib1.kt" }
val lib2 = compileLibrary("lib2", lib1) {
"lib2/lib2.file1.kt" copyTo "file1.kt"
"lib2/lib2.file2.kt" copyTo "file2.kt"
}
val main = compileToExecutable("main", lib1, lib2) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val lib1KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.kt", "test1")
val lib2File1KtCacheDir = getLibraryFileCache("lib2", "lib2/file1.kt", "test2")
val lib2File2KtCacheDir = getLibraryFileCache("lib2", "lib2/file2.kt", "test2")
assertTrue(lib1KtCacheDir.exists())
assertTrue(lib2File1KtCacheDir.exists())
assertTrue(lib2File2KtCacheDir.exists())
val modified11 = lib1KtCacheDir.lastModified()
val modified21 = lib2File1KtCacheDir.lastModified()
val modified22 = lib2File2KtCacheDir.lastModified()
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib2>'s file2 cache will be recompiled after changing lib1/lib1.kt.
val lib11 = compileLibrary("lib1") { "lib1/lib1.1.kt" copyTo "lib1.kt" }
val lib21 = compileLibrary("lib2", lib11) {
"lib2/lib2.file1.kt" copyTo "file1.kt"
"lib2/lib2.file2.kt" copyTo "file2.kt"
}
val main1 = compileToExecutable("main", lib11, lib21) { "main/main.1.kt" copyTo "main.kt" }
assertTrue(main1.executableFile.exists())
assertTrue(lib1KtCacheDir.exists())
assertTrue(lib2File1KtCacheDir.exists())
assertTrue(lib2File2KtCacheDir.exists())
assertNotEquals(modified11, lib1KtCacheDir.lastModified())
assertEquals(modified21, lib2File1KtCacheDir.lastModified())
assertNotEquals(modified22, lib2File2KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("addMethodToOpenClass1")
fun addMethodToOpenClass1() = withRootDir(File("$TEST_SUITE_PATH/addMethodToOpenClass1")) {
val lib1 = compileLibrary("lib1") { "lib1/lib1.kt" copyTo "lib1.kt" }
val lib2 = compileLibrary("lib2", lib1) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main = compileToExecutable("main", lib1, lib2) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val lib1KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.kt", "test1")
val lib2KtCacheDir = getLibraryFileCache("lib2", "lib2/lib2.kt", "test2")
assertTrue(lib1KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
val modified1 = lib1KtCacheDir.lastModified()
val modified2 = lib2KtCacheDir.lastModified()
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib2>'s cache won't be recompiled after changing lib1/lib1.kt.
val lib11 = compileLibrary("lib1") { "lib1/lib1.1.kt" copyTo "lib1.kt" }
val lib21 = compileLibrary("lib2", lib11) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main1 = compileToExecutable("main", lib11, lib21) { "main/main.kt" copyTo "main.kt" }
assertTrue(main1.executableFile.exists())
assertTrue(lib1KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
assertNotEquals(modified1, lib1KtCacheDir.lastModified())
assertEquals(modified2, lib2KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("addMethodToOpenClass2")
fun addMethodToOpenClass2() = withRootDir(File("$TEST_SUITE_PATH/addMethodToOpenClass2")) {
val lib1 = compileLibrary("lib1") {
"lib1/lib1.file1.kt" copyTo "lib1.file1.kt"
"lib1/lib1.file2.kt" copyTo "lib1.file2.kt"
}
val lib2 = compileLibrary("lib2", lib1) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main = compileToExecutable("main", lib1, lib2) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val lib1File1KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.file1.kt", "test1")
val lib1File2KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.file2.kt", "test1")
val lib2KtCacheDir = getLibraryFileCache("lib2", "lib2/lib2.kt", "test2")
assertTrue(lib1File1KtCacheDir.exists())
assertTrue(lib1File2KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
val modified11 = lib1File1KtCacheDir.lastModified()
val modified12 = lib1File2KtCacheDir.lastModified()
val modified2 = lib2KtCacheDir.lastModified()
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib2>'s cache won't be recompiled after changing lib1/lib1.file1.kt.
val lib11 = compileLibrary("lib1") {
"lib1/lib1.file1.1.kt" copyTo "lib1.file1.kt"
"lib1/lib1.file2.kt" copyTo "lib1.file2.kt"
}
val lib21 = compileLibrary("lib2", lib11) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main1 = compileToExecutable("main", lib11, lib21) { "main/main.kt" copyTo "main.kt" }
assertTrue(main1.executableFile.exists())
assertTrue(lib1File1KtCacheDir.exists())
assertTrue(lib1File2KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
assertNotEquals(modified11, lib1File1KtCacheDir.lastModified())
assertNotEquals(modified12, lib1File2KtCacheDir.lastModified())
assertEquals(modified2, lib2KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("addMethodToInterface1")
fun addMethodToInterface1() = withRootDir(File("$TEST_SUITE_PATH/addMethodToInterface1")) {
val lib1 = compileLibrary("lib1") { "lib1/lib1.kt" copyTo "lib1.kt" }
val lib2 = compileLibrary("lib2", lib1) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main = compileToExecutable("main", lib1, lib2) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val lib1KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.kt", "test1")
val lib2KtCacheDir = getLibraryFileCache("lib2", "lib2/lib2.kt", "test2")
assertTrue(lib1KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
val modified1 = lib1KtCacheDir.lastModified()
val modified2 = lib2KtCacheDir.lastModified()
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib2>'s cache won't be recompiled after changing lib1/lib1.kt.
val lib11 = compileLibrary("lib1") { "lib1/lib1.1.kt" copyTo "lib1.kt" }
val lib21 = compileLibrary("lib2", lib11) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main1 = compileToExecutable("main", lib11, lib21) { "main/main.1.kt" copyTo "main.kt" }
assertTrue(main1.executableFile.exists())
assertTrue(lib1KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
assertNotEquals(modified1, lib1KtCacheDir.lastModified())
assertEquals(modified2, lib2KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
@Test
@TestMetadata("addMethodToInterface2")
fun addMethodToInterface2() = withRootDir(File("$TEST_SUITE_PATH/addMethodToInterface2")) {
val lib1 = compileLibrary("lib1") {
"lib1/lib1.file1.kt" copyTo "lib1.file1.kt"
"lib1/lib1.file2.kt" copyTo "lib1.file2.kt"
}
val lib2 = compileLibrary("lib2", lib1) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main = compileToExecutable("main", lib1, lib2) { "main/main.kt" copyTo "main.kt" }
// Check, <lib> has been compiled to cache.
assertTrue(main.executableFile.exists())
val lib1File1KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.file1.kt", "test1")
val lib1File2KtCacheDir = getLibraryFileCache("lib1", "lib1/lib1.file2.kt", "test1")
val lib2KtCacheDir = getLibraryFileCache("lib2", "lib2/lib2.kt", "test2")
assertTrue(lib1File1KtCacheDir.exists())
assertTrue(lib1File2KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
val modified11 = lib1File1KtCacheDir.lastModified()
val modified12 = lib1File2KtCacheDir.lastModified()
val modified2 = lib2KtCacheDir.lastModified()
runExecutableAndVerify(main.testCase, main.testExecutable)
// Check, <lib2>'s cache won't be recompiled after changing lib1/lib1.file1.kt.
val lib11 = compileLibrary("lib1") {
"lib1/lib1.file1.1.kt" copyTo "lib1.file1.kt"
"lib1/lib1.file2.kt" copyTo "lib1.file2.kt"
}
val lib21 = compileLibrary("lib2", lib11) { "lib2/lib2.kt" copyTo "lib2.kt" }
val main1 = compileToExecutable("main", lib11, lib21) { "main/main.1.kt" copyTo "main.kt" }
assertTrue(main1.executableFile.exists())
assertTrue(lib1File1KtCacheDir.exists())
assertTrue(lib1File2KtCacheDir.exists())
assertTrue(lib2KtCacheDir.exists())
assertNotEquals(modified11, lib1File1KtCacheDir.lastModified())
assertNotEquals(modified12, lib1File2KtCacheDir.lastModified())
assertEquals(modified2, lib2KtCacheDir.lastModified())
runExecutableAndVerify(main1.testCase, main1.testExecutable)
}
private inline fun withRootDir(rootDir: File, block: RootDirHolder.() -> Unit) = RootDirHolder(rootDir).block()
private inner class RootDirHolder(val rootDir: File) {
inline fun compileLibrary(
targetSrc: String,
vararg dependencies: TestCompilationArtifact.KLIB,
block: LibraryBuilder.() -> Unit
) = with(LibraryBuilder(this@IncrementalCompilationTest, rootDir, targetSrc, dependencies.asList())) {
block()
build()
}
inline fun compileToExecutable(
targetSrc: String,
vararg dependencies: TestCompilationArtifact.KLIB,
block: ExecutableBuilder.() -> Unit
) = with(ExecutableBuilder(this@IncrementalCompilationTest, rootDir, targetSrc, dependencies.asList())) {
externalLibsDir.mkdirs()
icCacheDir.mkdirs()
autoCacheDir.mkdirs()
+"-Xauto-cache-from=${externalLibsDir.absolutePath}"
+"-Xauto-cache-dir=${autoCacheDir.absolutePath}"
+"-Xic-cache-dir=${icCacheDir.absolutePath}"
+"-Xenable-incremental-compilation"
+"-verbose"
block()
build()
}
}
private val externalLibsDir: File get() = buildDir.resolve("external")
private val autoCacheDir: File get() = buildDir.resolve("__auto_cache__")
private val icCacheDir: File get() = buildDir.resolve("__ic_cache__")
private val cacheFlavor: String
get() = CacheMode.computeCacheDirName(
testRunSettings.get<KotlinNativeTargets>().testTarget,
"STATIC",
testRunSettings.get<OptimizationMode>() == OptimizationMode.DEBUG,
partialLinkageEnabled = false
)
private fun getLibraryFileCache(libName: String, libFileRelativePath: String, fqName: String): File {
val libCacheDir = icCacheDir.resolve(cacheFlavor).resolve("$libName-per-file-cache")
val fileId = cacheFileId(fqName, buildDir.resolve(libFileRelativePath).absolutePath)
return libCacheDir.resolve(fileId)
}
private fun cacheFileId(fqName: String, filePath: String) =
"${if (fqName == "") "ROOT" else fqName}.${filePath.hashCode().toString(Character.MAX_RADIX)}"
companion object {
const val TEST_SUITE_PATH = "native/native.tests/testData/caches/ic"
}
}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilati
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestExecutable
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunChecks
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.PipelineType
@@ -36,6 +37,83 @@ import java.io.File
private val DEFAULT_EXTRAS = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
internal abstract class ArtifactBuilder<T>(
val test: AbstractNativeSimpleTest,
val rootDir: File,
val targetSrc: String,
dependencies: List<TestCompilationArtifact.KLIB>
) {
private val buildDir = test.buildDir
var outputDir: String = ""
private val sources = mutableListOf<Pair<String, String>>()
private val dependencies = dependencies.toMutableList()
infix fun String.copyTo(to: String) {
sources.add(Pair(this, to))
}
fun dependsOn(klib: TestCompilationArtifact.KLIB) {
dependencies.add(klib)
}
protected abstract fun build(sourcesDir: File, outputDir: File, dependencies: List<TestCompilationArtifact.KLIB>): T
fun build(): T {
val targetSrc = buildDir.resolve(targetSrc)
targetSrc.deleteRecursively()
targetSrc.mkdirs()
val outputDir = if (outputDir == "") buildDir else buildDir.resolve(outputDir)
sources.forEach {
val source = rootDir.resolve(it.first)
val target = targetSrc.resolve(it.second)
target.mkdirs()
if (source.isFile)
source.copyTo(target, true)
else
source.copyRecursively(target, true)
}
return build(targetSrc, outputDir, dependencies)
}
}
internal class LibraryBuilder(
test: AbstractNativeSimpleTest,
rootDir: File,
targetSrc: String,
dependencies: List<TestCompilationArtifact.KLIB>
) : ArtifactBuilder<TestCompilationArtifact.KLIB>(test, rootDir, targetSrc, dependencies) {
var libraryVersion: String? = null
override fun build(sourcesDir: File, outputDir: File, dependencies: List<TestCompilationArtifact.KLIB>) =
test.compileToLibrary(
sourcesDir,
outputDir,
freeCompilerArgs = libraryVersion?.let { TestCompilerArgs(listOf("-library-version=$it")) } ?: TestCompilerArgs.EMPTY,
dependencies
)
}
internal class ExecutableBuilder(
test: AbstractNativeSimpleTest,
rootDir: File,
targetSrc: String,
dependencies: List<TestCompilationArtifact.KLIB>
) : ArtifactBuilder<CompiledExecutable>(test, rootDir, targetSrc, dependencies) {
private val freeCompilerArgs = mutableListOf<String>()
operator fun String.unaryPlus() {
freeCompilerArgs.add(this)
}
override fun build(sourcesDir: File, outputDir: File, dependencies: List<TestCompilationArtifact.KLIB>) =
test.compileToExecutable(
sourcesDir,
freeCompilerArgs = if (freeCompilerArgs.isEmpty()) TestCompilerArgs.EMPTY else TestCompilerArgs(freeCompilerArgs),
dependencies
)
}
internal val AbstractNativeSimpleTest.buildDir: File get() = testRunSettings.get<SimpleTestDirectories>().testBuildDir
internal fun TestCompilationArtifact.KLIB.asLibraryDependency() =
@@ -54,19 +132,42 @@ internal fun AbstractNativeSimpleTest.compileToLibrary(
sourcesDir: File,
outputDir: File,
vararg dependencies: TestCompilationArtifact.KLIB
): TestCompilationArtifact.KLIB = compileToLibrary(sourcesDir, outputDir, TestCompilerArgs.EMPTY, dependencies.asList())
internal fun AbstractNativeSimpleTest.compileToLibrary(
sourcesDir: File,
outputDir: File,
freeCompilerArgs: TestCompilerArgs,
dependencies: List<TestCompilationArtifact.KLIB>
): TestCompilationArtifact.KLIB {
val testCase: TestCase = generateTestCaseWithSingleModule(sourcesDir)
val testCase: TestCase = generateTestCaseWithSingleModule(sourcesDir, freeCompilerArgs)
val compilationResult = compileToLibrary(testCase, outputDir, dependencies.map { it.asLibraryDependency() })
return compilationResult.resultingArtifact
}
internal class CompiledExecutable(
val testCase: TestCase,
val compilationResult: TestCompilationResult.Success<out TestCompilationArtifact.Executable>
) {
val executableFile: File get() = compilationResult.resultingArtifact.executableFile
val testExecutable by lazy { TestExecutable.fromCompilationResult(testCase, compilationResult) }
}
internal fun AbstractNativeSimpleTest.compileToExecutable(
sourcesDir: File,
freeCompilerArgs: TestCompilerArgs,
vararg dependencies: TestCompilationArtifact.KLIB
): TestCompilationResult<out TestCompilationArtifact.Executable> {
) = compileToExecutable(sourcesDir, freeCompilerArgs, dependencies.asList())
internal fun AbstractNativeSimpleTest.compileToExecutable(
sourcesDir: File,
freeCompilerArgs: TestCompilerArgs,
dependencies: List<TestCompilationArtifact.KLIB>
): CompiledExecutable {
val testCase: TestCase = generateTestCaseWithSingleModule(sourcesDir, freeCompilerArgs)
return compileToExecutable(testCase, dependencies.map { it.asLibraryDependency() })
val compilationResult = compileToExecutable(testCase, dependencies.map { it.asLibraryDependency() })
return CompiledExecutable(testCase, compilationResult.assertSuccess())
}
internal fun AbstractNativeSimpleTest.compileToExecutable(testCase: TestCase, vararg dependencies: TestCompilationDependency<*>) =