diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalCacheVersionChangedTest.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalCacheVersionChangedTest.kt index 536f6d91219..e348885d2e4 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalCacheVersionChangedTest.kt +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalCacheVersionChangedTest.kt @@ -17,10 +17,12 @@ package org.jetbrains.kotlin.jps.build import org.jetbrains.jps.incremental.ModuleBuildTarget +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.Modification +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.ModifyContent import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider abstract class AbstractIncrementalCacheVersionChangedTest : AbstractIncrementalJpsTest(allowNoFilesWithSuffixInTestData = true) { - override fun performAdditionalModifications(modifications: List) { + override fun performAdditionalModifications(modifications: List) { val modifiedFiles = modifications.filterIsInstance().map { it.path } val paths = projectDescriptor.dataManager.dataPaths val targets = projectDescriptor.allModuleTargets diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt index 1e27566a945..673cfb30222 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt @@ -47,6 +47,10 @@ import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.incremental.LookupSymbol import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.jps.build.classFilesComparison.assertEqualDirectories +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.Modification +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.TouchPolicy +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.copyTestSources +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.getModificationsToPerform import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageProvider import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget import org.jetbrains.kotlin.jps.incremental.getKotlinCache @@ -75,9 +79,6 @@ abstract class AbstractIncrementalJpsTest( val DEBUG_LOGGING_ENABLED = System.getProperty("debug.logging.enabled") == "true" - private val COMMANDS = listOf("new", "touch", "delete") - private val COMMANDS_AS_REGEX_PART = COMMANDS.joinToString("|") - private val COMMANDS_AS_MESSAGE_PART = COMMANDS.joinToString("/") { "\".$it\"" } private val BUILD_LOG_FILE_NAME = "build.log" } @@ -91,7 +92,7 @@ abstract class AbstractIncrementalJpsTest( protected var lookupsDuringTest: MutableSet by Delegates.notNull() - protected val mapWorkingToOriginalFile: MutableMap = hashMapOf() + protected var mapWorkingToOriginalFile: MutableMap = hashMapOf() protected open val experimentalBuildLogFileName = "experimental-ic-build.log" @@ -207,68 +208,6 @@ abstract class AbstractIncrementalJpsTest( return build(CompileScopeTestBuilder.rebuild().allModules(), checkLookups = false) } - private fun getModificationsToPerform(moduleNames: Collection?): List> { - - fun getModificationsForIteration(newSuffix: String, touchSuffix: String, deleteSuffix: String): List { - - fun getDirPrefix(fileName: String): String { - val underscore = fileName.indexOf("_") - - if (underscore != -1) { - val module = fileName.substring(0, underscore) - - assert(moduleNames != null) { "File name has module prefix, but multi-module environment is absent" } - assert(module in moduleNames!!) { "Module not found for file with prefix: $fileName" } - - return "$module/src" - } - - assert(moduleNames == null) { "Test is multi-module, but file has no module prefix: $fileName" } - return "src" - } - - val modifications = ArrayList() - for (file in testDataDir.listFiles()!!) { - val fileName = file.name - - if (fileName.endsWith(newSuffix)) { - modifications.add(ModifyContent(getDirPrefix(fileName) + "/" + fileName.removeSuffix(newSuffix), file)) - } - if (fileName.endsWith(touchSuffix)) { - modifications.add(TouchFile(getDirPrefix(fileName) + "/" + fileName.removeSuffix(touchSuffix))) - } - if (fileName.endsWith(deleteSuffix)) { - modifications.add(DeleteFile(getDirPrefix(fileName) + "/" + fileName.removeSuffix(deleteSuffix))) - } - } - return modifications - } - - val haveFilesWithoutNumbers = testDataDir.listFiles { it -> it.name.matches(".+\\.($COMMANDS_AS_REGEX_PART)$".toRegex()) }?.isNotEmpty() ?: false - val haveFilesWithNumbers = testDataDir.listFiles { it -> it.name.matches(".+\\.($COMMANDS_AS_REGEX_PART)\\.\\d+$".toRegex()) }?.isNotEmpty() ?: false - - if (haveFilesWithoutNumbers && haveFilesWithNumbers) { - fail("Bad test data format: files ending with both unnumbered and numbered $COMMANDS_AS_MESSAGE_PART were found") - } - if (!haveFilesWithoutNumbers && !haveFilesWithNumbers) { - if (allowNoFilesWithSuffixInTestData) { - return listOf(listOf()) - } - else { - fail("Bad test data format: no files ending with $COMMANDS_AS_MESSAGE_PART found") - } - } - - if (haveFilesWithoutNumbers) { - return listOf(getModificationsForIteration(".new", ".touch", ".delete")) - } - else { - return (1..10) - .map { getModificationsForIteration(".new.$it", ".touch.$it", ".delete.$it") } - .filter { it.isNotEmpty() } - } - } - private fun rebuildAndCheckOutput(makeOverallResult: MakeResult) { val outDir = File(getAbsolutePath("out")) val outAfterMake = File(getAbsolutePath("out-after-make")) @@ -430,8 +369,8 @@ abstract class AbstractIncrementalJpsTest( private fun performModificationsAndMake(moduleNames: Set?): List { val results = arrayListOf() + val modifications = getModificationsToPerform(testDataDir, moduleNames, allowNoFilesWithSuffixInTestData, TouchPolicy.TIMESTAMP) - val modifications = getModificationsToPerform(moduleNames) for (step in modifications) { step.forEach { it.perform(workDir, mapWorkingToOriginalFile) } performAdditionalModifications(step) @@ -452,16 +391,13 @@ abstract class AbstractIncrementalJpsTest( // null means one module private fun configureModules(): Set? { - - fun prepareSources(relativePathToSrc: String, filePrefix: String) { - val srcDir = File(workDir, relativePathToSrc) - FileUtil.copyDir(testDataDir, srcDir) { - it.isDirectory || it.name.startsWith(filePrefix) && (it.name.endsWith(".kt") || it.name.endsWith(".java")) - } - - srcDir.walk().forEach { mapWorkingToOriginalFile[it] = File(testDataDir, filePrefix + it.name) } - - preProcessSources(srcDir) + fun prepareModuleSources(moduleName: String?) { + val sourceDirName = moduleName?.let { "$it/src" } ?: "src" + val filePrefix = moduleName?.let { "${it}_" } ?: "" + val sourceDestinationDir = File(workDir, sourceDirName) + val sourcesMapping = copyTestSources(testDataDir, sourceDestinationDir, filePrefix) + mapWorkingToOriginalFile.putAll(sourcesMapping) + preProcessSources(sourceDestinationDir) } var moduleNames: Set? @@ -469,11 +405,11 @@ abstract class AbstractIncrementalJpsTest( val jdk = addJdk("my jdk") val moduleDependencies = readModuleDependencies() + mapWorkingToOriginalFile = hashMapOf() + if (moduleDependencies == null) { addModule("module", arrayOf(getAbsolutePath("src")), null, null, jdk) - - prepareSources(relativePathToSrc = "src", filePrefix = "") - + prepareModuleSources(moduleName = null) moduleNames = null } else { @@ -490,8 +426,7 @@ abstract class AbstractIncrementalJpsTest( } for (module in nameToModule.values) { - val moduleName = module.name - prepareSources(relativePathToSrc = "$moduleName/src", filePrefix = moduleName + "_") + prepareModuleSources(module.name) } moduleNames = nameToModule.keys @@ -501,6 +436,7 @@ abstract class AbstractIncrementalJpsTest( return moduleNames } + protected open fun preProcessSources(srcDir: File) { } @@ -548,51 +484,6 @@ abstract class AbstractIncrementalJpsTest( logBuf.append(KotlinTestUtils.replaceHashWithStar(message!!.replace("^$rootPath/".toRegex(), " "))).append('\n') } } - - protected abstract class Modification(val path: String) { - abstract fun perform(workDir: File, mapping: MutableMap) - - override fun toString(): String = "${javaClass.simpleName} $path" - } - - protected class ModifyContent(path: String, val dataFile: File) : Modification(path) { - override fun perform(workDir: File, mapping: MutableMap) { - val file = File(workDir, path) - - val oldLastModified = file.lastModified() - file.delete() - dataFile.copyTo(file) - - val newLastModified = file.lastModified() - if (newLastModified <= oldLastModified) { - //Mac OS and some versions of Linux truncate timestamp to nearest second - file.setLastModified(oldLastModified + 1000) - } - - mapping[file] = dataFile - } - } - - protected class TouchFile(path: String) : Modification(path) { - override fun perform(workDir: File, mapping: MutableMap) { - val file = File(workDir, path) - - val oldLastModified = file.lastModified() - //Mac OS and some versions of Linux truncate timestamp to nearest second - file.setLastModified(Math.max(System.currentTimeMillis(), oldLastModified + 1000)) - } - } - - protected class DeleteFile(path: String) : Modification(path) { - override fun perform(workDir: File, mapping: MutableMap) { - val fileToDelete = File(workDir, path) - if (!fileToDelete.delete()) { - throw AssertionError("Couldn't delete $fileToDelete") - } - - mapping.remove(fileToDelete) - } - } } internal val ProjectDescriptor.allModuleTargets: Collection diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalLazyCachesTest.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalLazyCachesTest.kt index 9850719ebd1..6be9be31d5b 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalLazyCachesTest.kt +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalLazyCachesTest.kt @@ -21,9 +21,11 @@ import org.jetbrains.jps.builders.BuildTarget import org.jetbrains.jps.builders.storage.BuildDataPaths import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.incremental.CacheVersion -import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner -import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME +import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.Modification +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.ModifyContent +import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget import org.jetbrains.kotlin.utils.Printer import java.io.File @@ -40,11 +42,11 @@ abstract class AbstractIncrementalLazyCachesTest : AbstractIncrementalJpsTest() UsefulTestCase.assertSameLinesWithFile(expectedFile.canonicalPath, actual) } - override fun performAdditionalModifications(modifications: List) { + override fun performAdditionalModifications(modifications: List) { super.performAdditionalModifications(modifications) for (modification in modifications) { - if (modification !is AbstractIncrementalJpsTest.ModifyContent) continue + if (modification !is ModifyContent) continue val name = File(modification.path).name diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/IncrementalProjectPathCaseChangedTest.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/IncrementalProjectPathCaseChangedTest.kt index 717cef06ac3..0e6d3f1ebf4 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/IncrementalProjectPathCaseChangedTest.kt +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/IncrementalProjectPathCaseChangedTest.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.jps.build import com.intellij.openapi.util.SystemInfoRt import org.jetbrains.jps.model.java.JavaSourceRootType +import org.jetbrains.kotlin.jps.build.incrementalModificationUtils.Modification class IncrementalProjectPathCaseChangedTest : AbstractIncrementalJpsTest(checkDumpsCaseInsensitively = true) { fun testProjectPathCaseChanged() { @@ -36,7 +37,7 @@ class IncrementalProjectPathCaseChangedTest : AbstractIncrementalJpsTest(checkDu super.doTest(testDataPath) } - override fun performAdditionalModifications(modifications: List) { + override fun performAdditionalModifications(modifications: List) { val module = myProject.modules[0] val sourceRoot = module.sourceRoots[0].url assert(sourceRoot.endsWith("/src")) diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/incrementalModificationUtils/incrementalModificationUtils.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/incrementalModificationUtils/incrementalModificationUtils.kt new file mode 100644 index 00000000000..438d0dee717 --- /dev/null +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/incrementalModificationUtils/incrementalModificationUtils.kt @@ -0,0 +1,161 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.jps.build.incrementalModificationUtils + +import com.intellij.openapi.util.io.FileUtil +import org.jetbrains.jps.builders.JpsBuildTestCase +import java.io.File +import java.util.* + +private val COMMANDS = listOf("new", "touch", "delete") +private val COMMANDS_AS_REGEX_PART = COMMANDS.joinToString("|") +private val COMMANDS_AS_MESSAGE_PART = COMMANDS.joinToString("/") { "\".$it\"" } + +enum class TouchPolicy { + TIMESTAMP, + CHECKSUM +} + +fun copyTestSources(testDataDir: File, sourceDestinationDir: File, filePrefix: String): Map { + val mapping = hashMapOf() + FileUtil.copyDir(testDataDir, sourceDestinationDir) { + it.isDirectory || it.name.startsWith(filePrefix) && (it.name.endsWith(".kt") || it.name.endsWith(".java")) + } + + sourceDestinationDir.walk().forEach { mapping[it] = File(testDataDir, filePrefix + it.name) } + return mapping +} + +fun getModificationsToPerform( + testDataDir: File, + moduleNames: Collection?, + allowNoFilesWithSuffixInTestData: Boolean, + touchPolicy: TouchPolicy +): List> { + + fun getModificationsForIteration(newSuffix: String, touchSuffix: String, deleteSuffix: String): List { + + fun getDirPrefix(fileName: String): String { + val underscore = fileName.indexOf("_") + + if (underscore != -1) { + val module = fileName.substring(0, underscore) + + assert(moduleNames != null) { "File name has module prefix, but multi-module environment is absent" } + assert(module in moduleNames!!) { "Module not found for file with prefix: $fileName" } + + return "$module/src" + } + + assert(moduleNames == null) { "Test is multi-module, but file has no module prefix: $fileName" } + return "src" + } + + val modifications = ArrayList() + for (file in testDataDir.listFiles()!!) { + val fileName = file.name + + if (fileName.endsWith(newSuffix)) { + modifications.add(ModifyContent(getDirPrefix(fileName) + "/" + fileName.removeSuffix(newSuffix), file)) + } + if (fileName.endsWith(touchSuffix)) { + modifications.add(TouchFile(getDirPrefix(fileName) + "/" + fileName.removeSuffix(touchSuffix), touchPolicy)) + } + if (fileName.endsWith(deleteSuffix)) { + modifications.add(DeleteFile(getDirPrefix(fileName) + "/" + fileName.removeSuffix(deleteSuffix))) + } + } + return modifications + } + + val haveFilesWithoutNumbers = testDataDir.listFiles { it -> it.name.matches(".+\\.($COMMANDS_AS_REGEX_PART)$".toRegex()) }?.isNotEmpty() ?: false + val haveFilesWithNumbers = testDataDir.listFiles { it -> it.name.matches(".+\\.($COMMANDS_AS_REGEX_PART)\\.\\d+$".toRegex()) }?.isNotEmpty() ?: false + + if (haveFilesWithoutNumbers && haveFilesWithNumbers) { + JpsBuildTestCase.fail("Bad test data format: files ending with both unnumbered and numbered $COMMANDS_AS_MESSAGE_PART were found") + } + if (!haveFilesWithoutNumbers && !haveFilesWithNumbers) { + if (allowNoFilesWithSuffixInTestData) { + return listOf(listOf()) + } + else { + JpsBuildTestCase.fail("Bad test data format: no files ending with $COMMANDS_AS_MESSAGE_PART found") + } + } + + if (haveFilesWithoutNumbers) { + return listOf(getModificationsForIteration(".new", ".touch", ".delete")) + } + else { + return (1..10) + .map { getModificationsForIteration(".new.$it", ".touch.$it", ".delete.$it") } + .filter { it.isNotEmpty() } + } +} + +abstract class Modification(val path: String) { + abstract fun perform(workDir: File, mapping: MutableMap) + + override fun toString(): String = "${javaClass.simpleName} $path" +} + +class ModifyContent(path: String, val dataFile: File) : Modification(path) { + override fun perform(workDir: File, mapping: MutableMap) { + val file = File(workDir, path) + + val oldLastModified = file.lastModified() + file.delete() + dataFile.copyTo(file) + + val newLastModified = file.lastModified() + if (newLastModified <= oldLastModified) { + //Mac OS and some versions of Linux truncate timestamp to nearest second + file.setLastModified(oldLastModified + 1000) + } + + mapping[file] = dataFile + } +} + +class TouchFile(path: String, private val touchPolicy: TouchPolicy) : Modification(path) { + override fun perform(workDir: File, mapping: MutableMap) { + val file = File(workDir, path) + + when (touchPolicy) { + TouchPolicy.TIMESTAMP -> { + val oldLastModified = file.lastModified() + //Mac OS and some versions of Linux truncate timestamp to nearest second + file.setLastModified(Math.max(System.currentTimeMillis(), oldLastModified + 1000)) + } + TouchPolicy.CHECKSUM -> { + file.appendText(" ") + } + } + + } +} + +class DeleteFile(path: String) : Modification(path) { + override fun perform(workDir: File, mapping: MutableMap) { + val fileToDelete = File(workDir, path) + if (!fileToDelete.delete()) { + throw AssertionError("Couldn't delete $fileToDelete") + } + + mapping.remove(fileToDelete) + } +}