Decouple lookup tracker tests from JPS
This commit is contained in:
+35
@@ -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)
|
||||
}
|
||||
}
|
||||
+7
-4
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
+5
-14
@@ -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) {
|
||||
|
||||
+153
-32
@@ -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
|
||||
-13
@@ -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
|
||||
-15
@@ -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
|
||||
Reference in New Issue
Block a user