[go: nahoru, domu]

blob: cbb69a50cebe6118dcbfb4a52d8a4b72b6a3efae [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* 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 androidx.compose.plugins.kotlin
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
import org.jetbrains.kotlin.checkers.DiagnosedRange
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1
import org.jetbrains.kotlin.diagnostics.RenderedDiagnostic
import java.io.File
abstract class AbstractComposeDiagnosticsTest : AbstractCompilerTest() {
fun doTest(expectedText: String) {
doTest(expectedText, myEnvironment!!)
}
fun doTest(expectedText: String, environment: KotlinCoreEnvironment) {
val diagnosedRanges: MutableList<DiagnosedRange> = ArrayList()
val clearText = CheckerTestUtil.parseDiagnosedRanges(expectedText, diagnosedRanges)
val file =
createFile("test.kt", clearText, environment.project)
val files = listOf(file)
// Use the JVM version of the analyzer to allow using classes in .jar files
val moduleTrace = NoScopeRecordCliBindingTrace()
val result = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
environment.project,
files,
moduleTrace,
environment.configuration.copy().apply {
this.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
},
environment::createPackagePartProvider
)
// Collect the errors
val errors = result.bindingContext.diagnostics.all().toMutableList()
val message = StringBuilder()
// Ensure all the expected messages are there
val found = mutableSetOf<Diagnostic>()
for (range in diagnosedRanges) {
for (diagnostic in range.getDiagnostics()) {
val reportedDiagnostics = errors.filter { it.factoryName == diagnostic.name }
if (reportedDiagnostics.isNotEmpty()) {
val reportedDiagnostic =
reportedDiagnostics.find {
it.textRanges.find {
it.startOffset == range.start && it.endOffset == range.end
} != null
}
if (reportedDiagnostic == null) {
val firstRange = reportedDiagnostics.first().textRanges.first()
message.append(" Error ${diagnostic.name} reported at ${
firstRange.startOffset
}-${firstRange.endOffset} but expected at ${range.start}-${range.end}\n")
message.append(
sourceInfo(
clearText,
firstRange.startOffset, firstRange.endOffset,
" "
)
)
} else {
errors.remove(reportedDiagnostic)
found.add(reportedDiagnostic)
}
} else {
message.append(" Diagnostic ${diagnostic.name} not reported, expected at ${
range.start
}\n")
message.append(
sourceInfo(
clearText,
range.start,
range.end,
" "
)
)
}
}
}
// Ensure only the expected errors are reported
for (diagnostic in errors) {
if (diagnostic !in found) {
val range = diagnostic.textRanges.first()
message.append(
" Unexpected diagnostic ${diagnostic.factoryName} reported at ${
range.startOffset
}\n"
)
message.append(
sourceInfo(
clearText,
range.startOffset,
range.endOffset,
" "
)
)
}
}
// Throw an error if anything was found that was not expected
if (message.length > 0) throw Exception("Mismatched errors:\n$message")
}
}
fun assertExists(file: File): File {
if (!file.exists()) {
throw IllegalStateException("'$file' does not exist. Run test from gradle")
}
return file
}
// Normalize the factory's name to find the name supplied by a plugin
@Suppress("UNCHECKED_CAST")
val Diagnostic.factoryName: String
inline get() {
if (factory.name == "PLUGIN_ERROR")
return (this as
DiagnosticWithParameters1<*, RenderedDiagnostic<*>>).a.diagnostic.factory.name
if (factory.name == "PLUGIN_WARNING")
return (this as
DiagnosticWithParameters1<*, RenderedDiagnostic<*>>).a.diagnostic.factory.name
return factory.name
}
fun String.lineStart(offset: Int): Int {
return this.lastIndexOf('\n', offset) + 1
}
fun String.lineEnd(offset: Int): Int {
val result = this.indexOf('\n', offset)
return if (result < 0) this.length else result
}
// Return the source line that contains the given range with the range underlined with '~'s
fun sourceInfo(clearText: String, start: Int, end: Int, prefix: String = ""): String {
val lineStart = clearText.lineStart(start)
val lineEnd = clearText.lineEnd(start)
val displayEnd = if (end > lineEnd) lineEnd else end
return prefix + clearText.substring(lineStart, lineEnd) + "\n" +
prefix + " ".repeat(start - lineStart) + "~".repeat(displayEnd - start) + "\n"
}