diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractKotlinJpsBuildTestCase.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractKotlinJpsBuildTestCase.kt index 05cd0ae62ab..9f2da53372a 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractKotlinJpsBuildTestCase.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractKotlinJpsBuildTestCase.kt @@ -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, exported: Boolean = false): JpsLibrary { return addDependency(modules, KotlinJpsLibrary.JvmStdLib, exported) diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt index 2c033ca0503..1ddb270fb8d 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt @@ -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() @@ -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 "" - 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()?.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() diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestBase.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestBase.kt new file mode 100644 index 00000000000..9c095a91285 --- /dev/null +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestBase.kt @@ -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()?.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 "" + val builder = StringBuilder() + for (file in files) { + builder.append(" * ").append(file.name).append("\n") + } + return builder.toString() + } + } +} \ No newline at end of file diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestIncremental.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestIncremental.kt index 52c1d681c1d..4150ec6a78d 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestIncremental.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTestIncremental.kt @@ -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()) diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/RelocatableJpsCachesTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/RelocatableJpsCachesTest.kt new file mode 100644 index 00000000000..e20a4493e95 --- /dev/null +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/RelocatableJpsCachesTest.kt @@ -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) { + 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 + ) { + 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() + 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() + } + } +} \ No newline at end of file