Decouple lookup tracker tests from JPS

This commit is contained in:
Alexey Tsvetkov
2017-07-31 14:11:07 +03:00
parent bb2fab5b5d
commit 485e2345a9
16 changed files with 276 additions and 86 deletions
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2017 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.compilerRunner
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
class MessageCollectorToOutputItemsCollectorAdapter(
private val delegate: MessageCollector,
private val outputCollector: OutputItemsCollector
) : MessageCollector by delegate {
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
// TODO: consider adding some other way of passing input -> output mapping from compiler, e.g. dedicated service
OutputMessageUtil.parseOutputMessage(message)?.let {
outputCollector.add(it.sourceFiles, it.outputFile)
}
delegate.report(severity, message, location)
}
}
@@ -128,13 +128,13 @@ fun getModificationsToPerform(
}
abstract class Modification(val path: String) {
abstract fun perform(workDir: File, mapping: MutableMap<File, File>)
abstract fun perform(workDir: File, mapping: MutableMap<File, File>): File?
override fun toString(): String = "${this::class.java.simpleName} $path"
}
class ModifyContent(path: String, val dataFile: File) : Modification(path) {
override fun perform(workDir: File, mapping: MutableMap<File, File>) {
override fun perform(workDir: File, mapping: MutableMap<File, File>): File? {
val file = File(workDir, path)
val oldLastModified = file.lastModified()
@@ -148,11 +148,12 @@ class ModifyContent(path: String, val dataFile: File) : Modification(path) {
}
mapping[file] = dataFile
return file
}
}
class TouchFile(path: String, private val touchPolicy: TouchPolicy) : Modification(path) {
override fun perform(workDir: File, mapping: MutableMap<File, File>) {
override fun perform(workDir: File, mapping: MutableMap<File, File>): File? {
val file = File(workDir, path)
when (touchPolicy) {
@@ -166,16 +167,18 @@ class TouchFile(path: String, private val touchPolicy: TouchPolicy) : Modificati
}
}
return file
}
}
class DeleteFile(path: String) : Modification(path) {
override fun perform(workDir: File, mapping: MutableMap<File, File>) {
override fun perform(workDir: File, mapping: MutableMap<File, File>): File? {
val fileToDelete = File(workDir, path)
if (!fileToDelete.delete()) {
throw AssertionError("Couldn't delete $fileToDelete")
}
mapping.remove(fileToDelete)
return null
}
}
+1
View File
@@ -24,5 +24,6 @@
<orderEntry type="module" module-name="js.translator" scope="TEST" />
<orderEntry type="module" module-name="preloader" scope="TEST" />
<orderEntry type="library" scope="PROVIDED" name="native-platform-uberjar" level="project" />
<orderEntry type="module" module-name="compiler-runner" scope="TEST" />
</component>
</module>
@@ -74,6 +74,7 @@ abstract class AbstractIncrementalJpsTest(
protected lateinit var testDataDir: File
protected lateinit var workDir: File
protected lateinit var projectDescriptor: ProjectDescriptor
// is used to compare lookup dumps in a human readable way (lookup symbols are hashed in an actual lookup storage)
protected lateinit var lookupsDuringTest: MutableSet<LookupSymbol>
private var isICEnabledBackup: Boolean = false
@@ -132,17 +133,12 @@ abstract class AbstractIncrementalJpsTest(
protected open val mockConstantSearch: Callbacks.ConstantAffectionResolver?
get() = null
private fun createLookupTracker(): TestLookupTracker = TestLookupTracker()
protected open fun checkLookups(@Suppress("UNUSED_PARAMETER") lookupTracker: LookupTracker, compiledFiles: Set<File>) {
}
private fun build(scope: CompileScopeTestBuilder = CompileScopeTestBuilder.make().allModules(), checkLookups: Boolean = true): MakeResult {
private fun build(scope: CompileScopeTestBuilder = CompileScopeTestBuilder.make().allModules()): MakeResult {
val workDirPath = FileUtil.toSystemIndependentName(workDir.absolutePath)
val logger = MyLogger(workDirPath)
projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger))
val lookupTracker = createLookupTracker()
val lookupTracker = TestLookupTracker()
projectDescriptor.project.setTestingContext(TestingContext(lookupTracker, logger))
try {
@@ -151,12 +147,7 @@ abstract class AbstractIncrementalJpsTest(
builder.addMessageHandler(buildResult)
builder.build(scope.build(), false)
if (checkLookups) {
checkLookups(lookupTracker, logger.compiledFiles)
}
val lookups = lookupTracker.lookups.map { LookupSymbol(it.name, it.scopeFqName) }
lookupsDuringTest.addAll(lookups)
lookupTracker.lookups.mapTo(lookupsDuringTest) { LookupSymbol(it.name, it.scopeFqName) }
if (!buildResult.isSuccessful) {
val errorMessages =
@@ -196,7 +187,7 @@ abstract class AbstractIncrementalJpsTest(
}
private fun rebuild(): MakeResult {
return build(CompileScopeTestBuilder.rebuild().allModules(), checkLookups = false)
return build(CompileScopeTestBuilder.rebuild().allModules())
}
private fun rebuildAndCheckOutput(makeOverallResult: MakeResult) {
@@ -17,40 +17,178 @@
package org.jetbrains.kotlin.jps.build
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.containers.HashMap
import com.intellij.util.containers.StringInterner
import org.jetbrains.kotlin.TestWithWorkingDir
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.Position
import org.jetbrains.kotlin.incremental.components.ScopeKind
import org.jetbrains.kotlin.incremental.isKotlinFile
import org.jetbrains.kotlin.incremental.makeModuleFile
import org.jetbrains.kotlin.incremental.testingUtils.TouchPolicy
import org.jetbrains.kotlin.incremental.testingUtils.copyTestSources
import org.jetbrains.kotlin.incremental.testingUtils.getModificationsToPerform
import org.jetbrains.kotlin.incremental.utils.TestMessageCollector
import org.jetbrains.kotlin.jps.incremental.createTestingCompilerEnvironment
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.io.*
import java.util.*
private val DECLARATION_KEYWORDS = listOf("interface", "class", "enum class", "object", "fun", "operator fun", "val", "var")
private val DECLARATION_STARTS_WITH = DECLARATION_KEYWORDS.map { it + " " }
abstract class AbstractLookupTrackerTest : AbstractIncrementalJpsTest(
allowNoFilesWithSuffixInTestData = true,
allowNoBuildLogFileInTestData = true
) {
abstract class AbstractLookupTrackerTest : TestWithWorkingDir() {
private val DECLARATION_KEYWORDS = listOf("interface", "class", "enum class", "object", "fun", "operator fun", "val", "var")
private val DECLARATION_STARTS_WITH = DECLARATION_KEYWORDS.map { it + " " }
// ignore KDoc like comments which starts with `/**`, example: /** text */
val COMMENT_WITH_LOOKUP_INFO = "/\\*[^*]+\\*/".toRegex()
private val COMMENT_WITH_LOOKUP_INFO = "/\\*[^*]+\\*/".toRegex()
override fun checkLookups(lookupTracker: LookupTracker, compiledFiles: Set<File>) {
if (lookupTracker !is TestLookupTracker) throw AssertionError("Expected TestLookupTracker, but: ${lookupTracker::class.java}")
private fun removeCommentsCommentsWithLookupInfo(files: Iterable<File>) {
files.forEach {
val content = it.readText()
it.writeText(content.replace(COMMENT_WITH_LOOKUP_INFO, ""))
}
}
protected lateinit var srcDir: File
protected lateinit var outDir: File
private var isICEnabledBackup: Boolean = false
override fun setUp() {
super.setUp()
srcDir = File(workingDir, "src").apply { mkdirs() }
outDir = File(workingDir, "out")
isICEnabledBackup = IncrementalCompilation.isEnabled()
IncrementalCompilation.setIsEnabled(true)
}
override fun tearDown() {
IncrementalCompilation.setIsEnabled(isICEnabledBackup)
super.tearDown()
}
fun doTest(path: String) {
val sb = StringBuilder()
fun CompilerOutput.logOutput(stepName: String) {
sb.appendln("==== $stepName ====")
sb.appendln("Compiling files:")
compiledFiles.map { it.toRelativeString(workingDir) }.sorted().forEach { sb.appendln(" " + it) }
sb.appendln("Exit code: $exitCode")
errors.forEach { sb.appendln(" " + it) }
sb.appendln()
}
val testDir = File(path)
val workToOriginalFileMap = HashMap(copyTestSources(testDir, srcDir, filePrefix = ""))
var dirtyFiles = srcDir.walk().filterTo(HashSet()) { it.isKotlinFile() }
val incrementalData = IncrementalData()
val steps = getModificationsToPerform(testDir, moduleNames = null, allowNoFilesWithSuffixInTestData = true, touchPolicy = TouchPolicy.CHECKSUM)
.filter { it.isNotEmpty() }
makeAndCheckLookups(dirtyFiles, workToOriginalFileMap, incrementalData).logOutput("INITIAL BUILD")
for ((i, modifications) in steps.withIndex()) {
dirtyFiles = modifications.mapNotNullTo(HashSet()) { it.perform(workingDir, workToOriginalFileMap) }
makeAndCheckLookups(dirtyFiles, workToOriginalFileMap, incrementalData).logOutput("STEP ${i + 1}")
}
val expectedBuildLog = File(testDir, "build.log")
UsefulTestCase.assertSameLinesWithFile(expectedBuildLog.canonicalPath, sb.toString())
}
private class CompilerOutput(
val exitCode: String,
val errors: List<String>,
val compiledFiles: Iterable<File>
)
private class IncrementalData(val sourceToOutput: MutableMap<File, MutableSet<File>> = hashMapOf())
private fun makeAndCheckLookups(
filesToCompile: Iterable<File>,
workingToOriginalFileMap: Map<File, File>,
incrementalData: IncrementalData
): CompilerOutput {
removeCommentsCommentsWithLookupInfo(filesToCompile)
for (dirtyFile in filesToCompile) {
incrementalData.sourceToOutput.remove(dirtyFile)?.forEach {
it.delete()
}
}
val lookupTracker = TestLookupTracker()
val messageCollector = TestMessageCollector()
val outputItemsCollector = OutputItemsCollectorImpl()
val services = Services.Builder().run {
register(LookupTracker::class.java, lookupTracker)
build()
}
val environment = createTestingCompilerEnvironment(messageCollector, outputItemsCollector, services)
val exitCode = runCompiler(filesToCompile, environment)
checkLookups(filesToCompile, lookupTracker, workingToOriginalFileMap)
for (output in outputItemsCollector.outputs) {
val outputFile = output.outputFile
if (outputFile.extension == "kotlin_module") continue
for (sourceFile in output.sourceFiles) {
val outputsForSource = incrementalData.sourceToOutput.getOrPut(sourceFile) { hashSetOf() }
outputsForSource.add(outputFile)
}
}
return CompilerOutput(exitCode.toString(), messageCollector.errors, filesToCompile)
}
private fun runCompiler(filesToCompile: Iterable<File>, env: JpsCompilerEnvironment): Any? {
val module = makeModuleFile(name = "test",
isTest = true,
outputDir = outDir,
sourcesToCompile = filesToCompile.toList(),
javaSourceRoots = listOf(srcDir),
classpath = listOf(outDir).filter { it.exists() },
friendDirs = emptyList())
outDir.mkdirs()
val args = arrayOf("-module", module.canonicalPath, "-Xreport-output-files")
try {
val stream = ByteArrayOutputStream()
val out = PrintStream(stream)
val exitCode = CompilerRunnerUtil.invokeExecMethod(K2JVMCompiler::class.java.name, args, env, out)
val reader = BufferedReader(StringReader(stream.toString()))
CompilerOutputParser.parseCompilerMessagesFromReader(env.messageCollector, reader, env.outputItemsCollector)
return exitCode
}
finally {
module.delete()
}
}
private fun checkLookups(
compiledFiles: Iterable<File>,
lookupTracker: TestLookupTracker,
workingToOriginalFileMap: Map<File, File>
) {
val fileToLookups = lookupTracker.lookups.groupBy { it.filePath }
fun checkLookupsInFile(expectedFile: File, actualFile: File) {
val independentFilePath = FileUtil.toSystemIndependentName(actualFile.path)
val lookupsFromFile = fileToLookups[independentFilePath] ?: return
val lookupsFromFile = fileToLookups[independentFilePath] ?: error("No lookups from compiled file: $actualFile")
val text = actualFile.readText()
val matchResult = COMMENT_WITH_LOOKUP_INFO.find(text)
if (matchResult != null) {
fail("File $actualFile unexpectedly contains multiline comments. In range ${matchResult.range} found: ${matchResult.value} in $text")
throw AssertionError("File $actualFile contains multiline comment in range ${matchResult.range}")
}
val lines = text.lines().toMutableList()
@@ -95,25 +233,8 @@ abstract class AbstractLookupTrackerTest : AbstractIncrementalJpsTest(
KotlinTestUtils.assertEqualsToFile(expectedFile, actual)
}
for (actualFile in compiledFiles) {
val expectedFile = mapWorkingToOriginalFile[actualFile]!!
checkLookupsInFile(expectedFile, actualFile)
}
}
override fun preProcessSources(srcDir: File) = dropBlockComments(srcDir)
private fun dropBlockComments(workSrcDir: File) {
for (file in workSrcDir.walkTopDown()) {
if (!file.isFile) continue
val original = file.readText()
val modified = original.replace(COMMENT_WITH_LOOKUP_INFO, "")
if (original != modified) {
file.writeText(modified)
}
for (file in compiledFiles) {
checkLookupsInFile(workingToOriginalFileMap[file]!!, file)
}
}
}
@@ -0,0 +1,11 @@
==== INITIAL BUILD ====
Compiling files:
src/bar.kt
src/constraints.kt
src/foo.kt
src/usages.kt
Exit code: COMPILATION_ERROR
Unresolved reference: vala
Unresolved reference: vara
Unresolved reference: foo
Unresolved reference: bar
@@ -1,13 +0,0 @@
Compiling files:
src/bar.kt
src/constraints.kt
src/foo.kt
src/usages.kt
End of files
Exit code: ABORT
------------------------------------------
COMPILATION FAILED
Unresolved reference: vala
Unresolved reference: vara
Unresolved reference: foo
Unresolved reference: bar
@@ -0,0 +1,16 @@
==== INITIAL BUILD ====
Compiling files:
src/comparison.kt
src/declarations.kt
src/delegateProperty.kt
src/mathematicalLike.kt
src/other.kt
Exit code: OK
==== STEP 1 ====
Compiling files:
src/comparison.kt
src/delegateProperty.kt
src/mathematicalLike.kt
src/other.kt
Exit code: OK
@@ -0,0 +1,7 @@
==== INITIAL BUILD ====
Compiling files:
src/genericType.kt
src/inferredType.kt
src/lambdaParameterType.kt
src/localInnerClassType.kt
Exit code: OK
@@ -0,0 +1,6 @@
==== INITIAL BUILD ====
Compiling files:
src/usages.kt
Exit code: COMPILATION_ERROR
Unresolved reference: IS
Unresolved reference: IS
@@ -1,8 +0,0 @@
Compiling files:
src/usages.kt
End of files
Exit code: ABORT
------------------------------------------
COMPILATION FAILED
Unresolved reference: IS
Unresolved reference: IS
@@ -0,0 +1,5 @@
==== INITIAL BUILD ====
Compiling files:
src/bar.kt
src/locals.kt
Exit code: OK
@@ -0,0 +1,13 @@
==== INITIAL BUILD ====
Compiling files:
src/bar.kt
src/baz.kt
src/foo1.kt
src/foo2.kt
Exit code: OK
==== STEP 1 ====
Compiling files:
src/foo1.kt
src/foo2.kt
Exit code: OK
@@ -0,0 +1,4 @@
==== INITIAL BUILD ====
Compiling files:
src/main.kt
Exit code: OK
@@ -0,0 +1,13 @@
==== INITIAL BUILD ====
Compiling files:
src/KotlinClass.kt
src/usages.kt
Exit code: COMPILATION_ERROR
Unresolved reference: setFoo
Val cannot be reassigned
Unresolved reference: bazBaz
Unresolved reference: bazBaz
Unresolved reference: boo
Unresolved reference: bazBaz
Unresolved reference: bazBaz
Unresolved reference: boo
@@ -1,15 +0,0 @@
Compiling files:
src/KotlinClass.kt
src/usages.kt
End of files
Exit code: ABORT
------------------------------------------
COMPILATION FAILED
Unresolved reference: setFoo
Val cannot be reassigned
Unresolved reference: bazBaz
Unresolved reference: bazBaz
Unresolved reference: boo
Unresolved reference: bazBaz
Unresolved reference: bazBaz
Unresolved reference: boo