Add stdlib API test + remove some extra IR stdlib API's

This commit is contained in:
Anton Bannykh
2020-05-11 20:46:52 +03:00
parent 1ed4324613
commit a18bfad53a
100 changed files with 34345 additions and 28 deletions
@@ -0,0 +1,298 @@
/*
* Copyright 2010-2020 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.js.test
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.js.messageCollectorLogger
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.backend.js.MainModule
import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries
import org.jetbrains.kotlin.ir.backend.js.loadIr
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE_ALL
import java.io.File
class ApiTest : KotlinTestWithEnvironment() {
private val STDLIB_PATH = "js/js.translator/testData/api/stdlib"
fun testStdlib() {
val project = environment.project
val configuration = environment.configuration
configuration.put(CommonConfigurationKeys.MODULE_NAME, "test")
configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB)
val config = JsConfig(project, configuration)
config.moduleDescriptors.single().checkRecursively(STDLIB_PATH)
}
private val STDLIB_IR_PATH = "js/js.translator/testData/api/stdlib-ir"
fun testIrStdlib() {
val fullRuntimeKlib: String = System.getProperty("kotlin.js.full.stdlib.path")
val resolvedLibraries =
jsResolveLibraries(listOf(File(fullRuntimeKlib).absolutePath), messageCollectorLogger(MessageCollector.NONE))
val project = environment.project
val configuration = environment.configuration
val moduleDescriptor = loadIr(
project,
MainModule.Klib(resolvedLibraries.getFullList().single()),
AnalyzerWithCompilerReport(configuration),
configuration,
resolvedLibraries,
listOf()
).module.descriptor
moduleDescriptor.checkRecursively(STDLIB_IR_PATH)
}
private val STDLIB_DIFF_PATH = "js/js.translator/testData/api/stdlib-diff"
private val onlyInStdlib = setOf(
"org.khronos.webgl",
"org.w3c.css.masking",
"org.w3c.dom",
"org.w3c.dom.clipboard",
"org.w3c.dom.css",
"org.w3c.dom.encryptedmedia",
"org.w3c.dom.events",
"org.w3c.dom.mediacapture",
"org.w3c.dom.mediasource",
"org.w3c.dom.parsing",
"org.w3c.dom.pointerevents",
"org.w3c.dom.svg",
"org.w3c.dom.url",
"org.w3c.fetch",
"org.w3c.files",
"org.w3c.notifications",
"org.w3c.performance",
"org.w3c.workers",
"org.w3c.xhr"
)
private val onlyInStdlibIr = setOf("testUtils")
fun testApiDifference() {
val files = STDLIB_PATH.listFiles()
val irFiles = STDLIB_IR_PATH.listFiles()
val allNames = (files + irFiles).map { it.name.dropLast(3).split('-').first() }.toSet()
for (name in allNames) {
val a = STDLIB_PATH.readFileText(name)
val b = STDLIB_IR_PATH.readFileText(name)
if (a == null) {
assertTrue("Package '$name' unexpectedly only present in IR stdlib", name in onlyInStdlibIr)
} else if (b == null) {
assertTrue("Package '$name' unexpectedly only present in old stdlib", name in onlyInStdlib)
} else {
val d = diff(a, b)
if (d.isNotBlank()) {
// Uncomment to overwrite the test data
// File("$STDLIB_DIFF_PATH/$name.kt").writeText(d)
KotlinTestUtils.assertEqualsToFile(File("$STDLIB_DIFF_PATH/$name.kt"), d)
}
}
}
}
private fun String.readFileText(name: String): String? {
val f = File("$this/$name.kt")
if (f.exists() && f.isFile) {
return f.readText()
} else {
var i = 0
var result: String? = null
while (true) {
val f = File("$this/$name-$i.kt")
if (!f.exists() || !f.isFile) break;
result = (result ?: "") + f.readText()
++i
}
return result
}
}
private fun diff(a: String, b: String): String {
val aLines = a.lines()
val bLines = b.lines()
val dx = Array(aLines.size + 1) { IntArray(bLines.size + 1) }
val dy = Array(aLines.size + 1) { IntArray(bLines.size + 1)}
val c = Array(aLines.size + 1) { IntArray(bLines.size + 1)}
for (i in 0..aLines.size) {
c[i][0] = i
dx[i][0] = -1
}
for (j in 0..bLines.size) {
c[0][j] = j
dy[0][j] = -1
}
for (i in 1..aLines.size) {
for (j in 1..bLines.size) {
if (c[i - 1][j] <= c[i][j - 1]) {
c[i][j] = c[i - 1][j] + 1
dx[i][j] = -1
} else {
c[i][j] = c[i][j - 1] + 1
dy[i][j] = -1
}
if (aLines[i - 1] == bLines[j - 1] && c[i - 1][j - 1] < c[i][j]) {
c[i][j] = c[i - 1][j - 1]
dx[i][j] = -1
dy[i][j] = -1
}
}
}
val result = mutableListOf<String>()
var x = aLines.size
var y = bLines.size
var hasDiff = false
while (x != 0 && y != 0) {
val tdx = dx[x][y]
val tdy = dy[x][y]
if (tdx != 0) {
if (tdy != 0) {
if (hasDiff) {
result += "--- ${x + 1},${y + 1} ---"
hasDiff = false
}
} else {
result += "- ${aLines[x - 1]}"
hasDiff = true
}
} else if (tdy != 0) {
result += "+ ${bLines[y - 1]}"
hasDiff = true
}
x += tdx
y += tdy
}
return result.reversed().joinToString("\n", postfix = "\n")
}
private fun String.listFiles(): Array<File> {
val dirFile = File(this)
assertTrue("Directory does not exist: ${dirFile.absolutePath}", dirFile.exists())
assertTrue("Not a directory: ${dirFile.absolutePath}", dirFile.isDirectory)
return dirFile.listFiles()!!
}
private fun ModuleDescriptor.checkRecursively(dir: String) {
val dirFile = File(dir)
assertTrue("Directory does not exist: ${dirFile.absolutePath}", dirFile.exists())
assertTrue("Not a directory: ${dirFile.absolutePath}", dirFile.isDirectory)
val files = dirFile.listFiles()!!.map { it.name }.toMutableSet()
allPackages().forEach { fqName ->
getPackage(fqName).serialize()?.let { serialized ->
serialized.forEachIndexed { index, part ->
val fileName =
(if (fqName.isRoot) "ROOT" else fqName.asString()) + (if (serialized.size == 1) "" else "-$index") + ".kt"
files -= fileName
// Uncomment to overwrite the test data
// File("$dir/$fileName").writeText(part)
KotlinTestUtils.assertEqualsToFile(File("$dir/$fileName"), part)
}
}
}
assertTrue("Extra files found: ${files}", files.isEmpty())
}
private fun ModuleDescriptor.allPackages(): Collection<FqName> {
val result = mutableListOf<FqName>()
fun impl(pkg: FqName) {
result += pkg
getSubPackagesOf(pkg) { true }.forEach { impl(it) }
}
impl(FqName.ROOT)
return result
}
private fun PackageViewDescriptor.serialize(): List<String>? {
val comparator = RecursiveDescriptorComparator(RECURSIVE_ALL.filterRecursion {
when {
it is MemberDescriptor && it.isExpect -> false
it is DeclarationDescriptorWithVisibility && !it.visibility.isPublicAPI -> false
it is CallableMemberDescriptor && !it.kind.isReal -> false
it is PackageViewDescriptor -> false
else -> true
}
}.renderDeclarationsFromOtherModules(true))
val serialized = comparator.serializeRecursively(this).trim()
val lines = serialized.lines()
if (lines.size <= 1) return null
if (lines.size > LINES_PER_FILE_CUTOFF) {
val result = mutableListOf<String>()
val sb = StringBuilder()
var cnt = 0
var bracketBalance = 0
for (d in lines) {
if (cnt > LINES_PER_FILE_CUTOFF && bracketBalance == 0 && (d.isBlank() || !d[0].isWhitespace())) {
result += sb.toString()
sb.clear()
cnt = 0
}
sb.append(d).append('\n')
++cnt
bracketBalance += d.count { it == '{' } - d.count { it == '}' }
}
if (sb.isNotBlank()) {
result += sb.toString()
}
return result
}
return listOf(serialized)
}
override fun createEnvironment(): KotlinCoreEnvironment {
return KotlinCoreEnvironment.createForTests(TestDisposable(), CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
}
}
// IDEA isn't able to show diff for files that are too big
private val LINES_PER_FILE_CUTOFF = 1000
@@ -88,10 +88,7 @@ abstract class BasicIrBoxTest(
isMainModule: Boolean,
skipDceDriven: Boolean
) {
val filesToCompile = units
.map { (it as TranslationUnit.SourceFile).file }
// TODO: split input files to some parts (global common, local common, test)
.filterNot { it.virtualFilePath.contains(COMMON_FILES_DIR_PATH) }
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
val runtimeKlibs = if (needsFullIrRuntime) listOf(fullRuntimeKlib, kotlinTestKLib) else listOf(defaultRuntimeKlib)