Rewrite test of relocatable JPS IC caches
* Caches are compared as files now. It is a more robust approach than comparing strings with dumped contents. E.g previous test ignored differences in keys ordering, because dumping caches to string was added for comparing caches after incremental and non-incremental builds, which cannot be compared without sorting keys (see KT-32674). * Calling setUp/tearDown twice within the same test instance was relatively hacky and fragile. Also it complicated adding new test cases.
This commit is contained in:
-9
@@ -73,15 +73,6 @@ abstract class AbstractKotlinJpsBuildTestCase : BaseKotlinJpsBuildTestCase() {
|
||||
companion object {
|
||||
val TEST_DATA_PATH = "jps-plugin/testData/"
|
||||
|
||||
@Throws(IOException::class)
|
||||
@JvmStatic
|
||||
protected fun copyTestDataToTmpDir(testDataDir: File): File {
|
||||
assert(testDataDir.exists()) { "Cannot find source folder " + testDataDir.absolutePath }
|
||||
val tmpDir = FileUtil.createTempDirectory("jps-build", null)
|
||||
FileUtil.copyDir(testDataDir, tmpDir)
|
||||
return tmpDir
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun addKotlinStdlibDependency(modules: Collection<JpsModule>, exported: Boolean = false): JpsLibrary {
|
||||
return addDependency(modules, KotlinJpsLibrary.JvmStdLib, exported)
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.google.common.collect.Lists
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.FileUtil.toSystemIndependentName
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
@@ -60,9 +59,9 @@ import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.jps.incremental.CacheAttributesDiff
|
||||
import org.jetbrains.kotlin.incremental.withIC
|
||||
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.LibraryDependency.*
|
||||
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTestBase.LibraryDependency.*
|
||||
import org.jetbrains.kotlin.jps.incremental.CacheAttributesDiff
|
||||
import org.jetbrains.kotlin.jps.model.kotlinCommonCompilerArguments
|
||||
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
|
||||
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
|
||||
@@ -82,15 +81,12 @@ import java.io.FileNotFoundException
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
|
||||
open class KotlinJpsBuildTest : KotlinJpsBuildTestBase() {
|
||||
companion object {
|
||||
private val PROJECT_NAME = "kotlinProject"
|
||||
private val ADDITIONAL_MODULE_NAME = "module2"
|
||||
private val JDK_NAME = "IDEA_JDK"
|
||||
|
||||
private val EXCLUDE_FILES = arrayOf("Excluded.class", "YetAnotherExcluded.class")
|
||||
private val NOTHING = arrayOf<String>()
|
||||
@@ -109,43 +105,6 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
|
||||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun assertFilesExistInOutput(module: JpsModule, vararg relativePaths: String) {
|
||||
for (path in relativePaths) {
|
||||
val outputFile = findFileInOutputDir(module, path)
|
||||
assertTrue("Output not written: " + outputFile.absolutePath + "\n Directory contents: \n" + dirContents(outputFile.parentFile), outputFile.exists())
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun findFileInOutputDir(module: JpsModule, relativePath: String): File {
|
||||
val outputUrl = JpsJavaExtensionService.getInstance().getOutputUrl(module, false)
|
||||
assertNotNull(outputUrl)
|
||||
val outputDir = File(JpsPathUtil.urlToPath(outputUrl))
|
||||
return File(outputDir, relativePath)
|
||||
}
|
||||
|
||||
|
||||
@JvmStatic
|
||||
protected fun assertFilesNotExistInOutput(module: JpsModule, vararg relativePaths: String) {
|
||||
val outputUrl = JpsJavaExtensionService.getInstance().getOutputUrl(module, false)
|
||||
assertNotNull(outputUrl)
|
||||
val outputDir = File(JpsPathUtil.urlToPath(outputUrl))
|
||||
for (path in relativePaths) {
|
||||
val outputFile = File(outputDir, path)
|
||||
assertFalse("Output directory \"" + outputFile.absolutePath + "\" contains \"" + path + "\"", outputFile.exists())
|
||||
}
|
||||
}
|
||||
|
||||
private fun dirContents(dir: File): String {
|
||||
val files = dir.listFiles() ?: return "<not found>"
|
||||
val builder = StringBuilder()
|
||||
for (file in files) {
|
||||
builder.append(" * ").append(file.name).append("\n")
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun klass(moduleName: String, classFqName: String): String {
|
||||
val outputDirPrefix = "out/production/$moduleName/"
|
||||
@@ -158,48 +117,6 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
annotation class WorkingDir(val name: String)
|
||||
|
||||
enum class LibraryDependency {
|
||||
NONE,
|
||||
JVM_MOCK_RUNTIME,
|
||||
JVM_FULL_RUNTIME,
|
||||
JS_STDLIB,
|
||||
}
|
||||
|
||||
protected lateinit var originalProjectDir: File
|
||||
private val expectedOutputFile: File
|
||||
get() = File(originalProjectDir, "expected-output.txt")
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
val currentTestMethod = this::class.members.firstOrNull { it.name == "test" + getTestName(false) }
|
||||
val workingDirFromAnnotation = currentTestMethod?.annotations?.filterIsInstance<WorkingDir>()?.firstOrNull()?.name
|
||||
val projDirPath = Paths.get(TEST_DATA_PATH, "general", workingDirFromAnnotation ?: getTestName(false))
|
||||
originalProjectDir = projDirPath.toFile()
|
||||
workDir = AbstractKotlinJpsBuildTestCase.copyTestDataToTmpDir(originalProjectDir)
|
||||
orCreateProjectDir
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
FileUtil.delete(workDir)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
override fun doGetProjectDir(): File = workDir
|
||||
|
||||
protected fun initProject(libraryDependency: LibraryDependency = NONE) {
|
||||
addJdk(JDK_NAME)
|
||||
loadProject(workDir.absolutePath + File.separator + PROJECT_NAME + ".ipr")
|
||||
|
||||
when (libraryDependency) {
|
||||
NONE -> {}
|
||||
JVM_MOCK_RUNTIME -> addKotlinMockRuntimeDependency()
|
||||
JVM_FULL_RUNTIME -> addKotlinStdlibDependency()
|
||||
JS_STDLIB -> addKotlinJavaScriptStdlibDependency()
|
||||
}
|
||||
}
|
||||
|
||||
fun doTest() {
|
||||
initProject(JVM_MOCK_RUNTIME)
|
||||
buildAllModules().assertSuccessful()
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.jps.build
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
open class KotlinJpsBuildTestBase : AbstractKotlinJpsBuildTestCase() {
|
||||
protected lateinit var originalProjectDir: File
|
||||
protected val expectedOutputFile: File
|
||||
get() = File(originalProjectDir, "expected-output.txt")
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
val currentTestMethod = this::class.members.firstOrNull { it.name == "test" + getTestName(false) }
|
||||
val workingDirFromAnnotation = currentTestMethod?.annotations?.filterIsInstance<WorkingDir>()?.firstOrNull()?.name
|
||||
val projDirPath = Paths.get(
|
||||
TEST_DATA_PATH,
|
||||
"general",
|
||||
workingDirFromAnnotation ?: getTestName(false)
|
||||
)
|
||||
originalProjectDir = projDirPath.toFile()
|
||||
workDir = copyTestDataToTmpDir(originalProjectDir)
|
||||
orCreateProjectDir
|
||||
}
|
||||
|
||||
protected open fun copyTestDataToTmpDir(testDataDir: File): File {
|
||||
assert(testDataDir.exists()) { "Cannot find source folder " + testDataDir.absolutePath }
|
||||
val tmpDir = FileUtil.createTempDirectory("jps-build", null)
|
||||
FileUtil.copyDir(testDataDir, tmpDir)
|
||||
return tmpDir
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
workDir.deleteRecursively()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
override fun doGetProjectDir(): File = workDir
|
||||
|
||||
annotation class WorkingDir(val name: String)
|
||||
|
||||
enum class LibraryDependency {
|
||||
NONE,
|
||||
JVM_MOCK_RUNTIME,
|
||||
JVM_FULL_RUNTIME,
|
||||
JS_STDLIB,
|
||||
}
|
||||
|
||||
protected fun initProject(libraryDependency: LibraryDependency = LibraryDependency.NONE) {
|
||||
addJdk(JDK_NAME)
|
||||
loadProject(workDir.absolutePath + File.separator + PROJECT_NAME + ".ipr")
|
||||
|
||||
when (libraryDependency) {
|
||||
LibraryDependency.NONE -> {}
|
||||
LibraryDependency.JVM_MOCK_RUNTIME -> addKotlinMockRuntimeDependency()
|
||||
LibraryDependency.JVM_FULL_RUNTIME -> addKotlinStdlibDependency()
|
||||
LibraryDependency.JS_STDLIB -> addKotlinJavaScriptStdlibDependency()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val JDK_NAME = "IDEA_JDK"
|
||||
const val PROJECT_NAME = "kotlinProject"
|
||||
|
||||
@JvmStatic
|
||||
protected fun assertFilesExistInOutput(module: JpsModule, vararg relativePaths: String) {
|
||||
for (path in relativePaths) {
|
||||
val outputFile = findFileInOutputDir(module, path)
|
||||
assertTrue("Output not written: " + outputFile.absolutePath + "\n Directory contents: \n" + dirContents(
|
||||
outputFile.parentFile
|
||||
), outputFile.exists())
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun findFileInOutputDir(module: JpsModule, relativePath: String): File {
|
||||
val outputUrl = JpsJavaExtensionService.getInstance().getOutputUrl(module, false)
|
||||
assertNotNull(outputUrl)
|
||||
val outputDir = File(JpsPathUtil.urlToPath(outputUrl))
|
||||
return File(outputDir, relativePath)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun assertFilesNotExistInOutput(module: JpsModule, vararg relativePaths: String) {
|
||||
val outputUrl = JpsJavaExtensionService.getInstance().getOutputUrl(module, false)
|
||||
assertNotNull(outputUrl)
|
||||
val outputDir = File(JpsPathUtil.urlToPath(outputUrl))
|
||||
for (path in relativePaths) {
|
||||
val outputFile = File(outputDir, path)
|
||||
assertFalse("Output directory \"" + outputFile.absolutePath + "\" contains \"" + path + "\"", outputFile.exists())
|
||||
}
|
||||
}
|
||||
|
||||
private fun dirContents(dir: File): String {
|
||||
val files = dir.listFiles() ?: return "<not found>"
|
||||
val builder = StringBuilder()
|
||||
for (file in files) {
|
||||
builder.append(" * ").append(file.name).append("\n")
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
-52
@@ -58,58 +58,6 @@ class KotlinJpsBuildTestIncremental : KotlinJpsBuildTest() {
|
||||
checkOutputFilesList(File(workDir, "out/production"))
|
||||
}
|
||||
|
||||
fun testRelocatableCaches() {
|
||||
fun buildAndGetMappings(): String {
|
||||
workDir.deleteRecursively()
|
||||
workDir = AbstractKotlinJpsBuildTestCase.copyTestDataToTmpDir(originalProjectDir)
|
||||
myDataStorageRoot.deleteRecursively()
|
||||
myDataStorageRoot.mkdirs()
|
||||
|
||||
initProject(LibraryDependency.JVM_FULL_RUNTIME)
|
||||
|
||||
val workDirPath = FileUtil.toSystemIndependentName(workDir.absolutePath)
|
||||
val logger = AbstractIncrementalJpsTest.MyLogger(workDirPath)
|
||||
val projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger))
|
||||
|
||||
val lookupTracker = TestLookupTracker()
|
||||
val testingContext = TestingContext(lookupTracker, buildLogger = null)
|
||||
myProject.setTestingContext(testingContext)
|
||||
|
||||
try {
|
||||
doBuild(projectDescriptor, CompileScopeTestBuilder.rebuild().allModules()).assertSuccessful()
|
||||
|
||||
assertFilesExistInOutput(
|
||||
myProject.modules.single(),
|
||||
"MainKt.class", "Foo.class", "FooChild.class", "utils/Utils.class"
|
||||
)
|
||||
|
||||
val kotlinContext = testingContext.kotlinCompileContext!!
|
||||
val lookups = lookupTracker.lookups.mapTo(HashSet()) { LookupSymbol(it.name, it.scopeFqName) }
|
||||
|
||||
return createKotlinCachesDump(projectDescriptor, kotlinContext, lookups)
|
||||
} finally {
|
||||
projectDescriptor.release()
|
||||
}
|
||||
}
|
||||
|
||||
val mappings1 = buildAndGetMappings()
|
||||
val projectDir1 = workDir
|
||||
tearDown()
|
||||
// hack to prevent setUp from creating the same dir after tearDown
|
||||
projectDir1.mkdirs()
|
||||
|
||||
try {
|
||||
setUp()
|
||||
val projectDir2 = workDir
|
||||
Assert.assertNotEquals(projectDir1, projectDir2)
|
||||
|
||||
val mappings2 = buildAndGetMappings()
|
||||
Assert.assertEquals(mappings1, mappings2)
|
||||
} finally {
|
||||
projectDir1.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
fun testJpsDaemonIC() {
|
||||
fun testImpl() {
|
||||
assertTrue("Daemon was not enabled!", isDaemonEnabled())
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.jps.build
|
||||
|
||||
import org.jetbrains.jps.builders.*
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.cmdline.ProjectDescriptor
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
||||
import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
|
||||
import org.jetbrains.kotlin.incremental.testingUtils.assertEqualDirectories
|
||||
import org.jetbrains.kotlin.jps.build.fixtures.EnableICFixture
|
||||
import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget
|
||||
import java.io.File
|
||||
import kotlin.reflect.KFunction1
|
||||
|
||||
class RelocatableJpsCachesTest : BaseKotlinJpsBuildTestCase() {
|
||||
private val enableICFixture = EnableICFixture()
|
||||
private lateinit var workingDir: File
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
enableICFixture.setUp()
|
||||
workingDir = createTempDir("RelocatableJpsCachesTest", getTestName(false))
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
workingDir.deleteRecursively()
|
||||
enableICFixture.tearDown()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
fun testRelocatableCaches() {
|
||||
buildTwiceAndCompare(RelocatableCacheTestCase::testRelocatableCaches)
|
||||
}
|
||||
|
||||
private fun buildTwiceAndCompare(testMethod: KFunction1<RelocatableCacheTestCase, Unit>) {
|
||||
val test1WorkingDir = workingDir.resolve("test1")
|
||||
val test1KotlinCachesDir = workingDir.resolve("test1KotlinCaches")
|
||||
val test2WorkingDir = workingDir.resolve("test2")
|
||||
val test2KotlinCachesDir = workingDir.resolve("test2KotlinCaches")
|
||||
|
||||
runTestAndCopyKotlinCaches(test1WorkingDir, test1KotlinCachesDir, testMethod)
|
||||
runTestAndCopyKotlinCaches(test2WorkingDir, test2KotlinCachesDir, testMethod)
|
||||
|
||||
assertEqualDirectories(test1KotlinCachesDir, test2KotlinCachesDir, forgiveExtraFiles = false)
|
||||
}
|
||||
|
||||
private fun runTestAndCopyKotlinCaches(
|
||||
projectWorkingDir: File,
|
||||
dirToCopyKotlinCaches: File,
|
||||
testMethod: KFunction1<RelocatableCacheTestCase, Unit>
|
||||
) {
|
||||
val testCase = object : RelocatableCacheTestCase(
|
||||
projectWorkingDir = projectWorkingDir,
|
||||
dirToCopyKotlinCaches = dirToCopyKotlinCaches
|
||||
) {
|
||||
override fun getName(): String = testMethod.name
|
||||
}
|
||||
testCase.exposedPrivateApi.setUp()
|
||||
|
||||
try {
|
||||
testMethod.call(testCase)
|
||||
} finally {
|
||||
testCase.exposedPrivateApi.tearDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the class should not be executed directly (hence it's abstract)
|
||||
abstract class RelocatableCacheTestCase(
|
||||
private val projectWorkingDir: File,
|
||||
private val dirToCopyKotlinCaches: File
|
||||
) : KotlinJpsBuildTestBase() {
|
||||
val exposedPrivateApi = ExposedPrivateApi()
|
||||
|
||||
fun testRelocatableCaches() {
|
||||
initProject(LibraryDependency.JVM_FULL_RUNTIME)
|
||||
buildAllModules().assertSuccessful()
|
||||
|
||||
assertFilesExistInOutput(
|
||||
myProject.modules.single(),
|
||||
"MainKt.class", "Foo.class", "FooChild.class", "utils/Utils.class"
|
||||
)
|
||||
}
|
||||
|
||||
override fun copyTestDataToTmpDir(testDataDir: File): File {
|
||||
testDataDir.copyRecursively(projectWorkingDir)
|
||||
return projectWorkingDir
|
||||
}
|
||||
|
||||
override fun doBuild(descriptor: ProjectDescriptor, scopeBuilder: CompileScopeTestBuilder?): BuildResult =
|
||||
super.doBuild(descriptor, scopeBuilder).also {
|
||||
copyKotlinCaches(descriptor)
|
||||
}
|
||||
|
||||
private fun copyKotlinCaches(descriptor: ProjectDescriptor) {
|
||||
val kotlinDataPaths = HashSet<File>()
|
||||
val dataPaths = descriptor.dataManager.dataPaths
|
||||
kotlinDataPaths.add(dataPaths.getTargetDataRoot(KotlinDataContainerTarget))
|
||||
|
||||
for (target in descriptor.buildTargetIndex.allTargets) {
|
||||
if (!target.isKotlinTarget(descriptor)) continue
|
||||
|
||||
val targetDataRoot = descriptor.dataManager.dataPaths.getTargetDataRoot(target)
|
||||
val kotlinDataRoot = targetDataRoot.resolve(KOTLIN_CACHE_DIRECTORY_NAME)
|
||||
assert(kotlinDataRoot.isDirectory) { "Kotlin data root '$kotlinDataRoot' is not a directory" }
|
||||
kotlinDataPaths.add(kotlinDataRoot)
|
||||
}
|
||||
|
||||
dirToCopyKotlinCaches.deleteRecursively()
|
||||
val originalStorageRoot = descriptor.dataManager.dataPaths.dataStorageRoot
|
||||
for (kotlinCacheRoot in kotlinDataPaths) {
|
||||
val relativePath = kotlinCacheRoot.relativeTo(originalStorageRoot).path
|
||||
val targetDir = dirToCopyKotlinCaches.resolve(relativePath)
|
||||
targetDir.parentFile.mkdirs()
|
||||
kotlinCacheRoot.copyRecursively(targetDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun BuildTarget<*>.isKotlinTarget(descriptor: ProjectDescriptor): Boolean {
|
||||
fun JavaSourceRootDescriptor.containsKotlinSources() = root.walk().any { it.isKotlinSourceFile }
|
||||
|
||||
if (this !is ModuleBuildTarget) return false
|
||||
|
||||
val rootDescriptors = computeRootDescriptors(
|
||||
descriptor.model,
|
||||
descriptor.moduleExcludeIndex,
|
||||
descriptor.ignoredFileIndex,
|
||||
descriptor.dataManager.dataPaths
|
||||
)
|
||||
|
||||
return rootDescriptors.any { it is JavaSourceRootDescriptor && it.containsKotlinSources() }
|
||||
}
|
||||
|
||||
// the famous Public Morozov pattern
|
||||
inner class ExposedPrivateApi {
|
||||
fun setUp() {
|
||||
this@RelocatableCacheTestCase.setUp()
|
||||
}
|
||||
|
||||
fun tearDown() {
|
||||
this@RelocatableCacheTestCase.tearDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user