From 16b3fedcd4f363c1afdc5810a4dd8beb6ff4e46a Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Thu, 14 Jan 2021 15:51:20 +0300 Subject: [PATCH] Created AbstractKlibBinaryCompatibilityTest and AbstractJsKlibBinaryCompatibilityTest A common test runner for klib binary compatibility tests and its js counterpart --- .../AbstractKlibBinaryCompatibilityTest.kt | 104 ++++++++ .../generators/tests/GenerateJsTests.kt | 12 +- .../AbstractJsKlibBinaryCompatibilityTest.kt | 111 +++++++++ ...sKlibBinaryCompatibilityTestGenerated.java | 222 ++++++++++++++++++ 4 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 compiler/tests-common/tests/org/jetbrains/kotlin/compatibility/binary/AbstractKlibBinaryCompatibilityTest.kt create mode 100644 js/js.tests/test/org/jetbrains/kotlin/js/test/compatibility/binary/AbstractJsKlibBinaryCompatibilityTest.kt create mode 100644 js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/compatibility/binary/JsKlibBinaryCompatibilityTestGenerated.java diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/compatibility/binary/AbstractKlibBinaryCompatibilityTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/compatibility/binary/AbstractKlibBinaryCompatibilityTest.kt new file mode 100644 index 00000000000..3a7df264152 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/compatibility/binary/AbstractKlibBinaryCompatibilityTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2021 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.compatibility.binary + +import org.jetbrains.kotlin.test.* +import org.jetbrains.kotlin.test.util.KtTestUtil +import org.jetbrains.kotlin.utils.DFS +import java.io.File + +class TestFile(val module: TestModule, fileName: String, text: String, directives: Directives) + : KotlinBaseTest.TestFile(fileName, text, directives) { + init { + module.files.add(this) + } + val version: Int? = directives["VERSION"]?.toInt() +} + +class TestModule(name: String, dependenciesSymbols: List, friends: List) + : KotlinBaseTest.TestModule(name, dependenciesSymbols, friends) { + + val files = mutableListOf() + val hasVersions get() = files.any { it.version != null } + fun versionFiles(version: Int) = files.filter { it.version == null || it.version == version } +} + +abstract class AbstractKlibBinaryCompatibilityTest : KotlinTestWithEnvironment() { + + private val pathToRootOutputDir = System.getProperty("kotlin.js.test.root.out.dir") ?: error("'kotlin.js.test.root.out.dir' is not set") + private val testGroupSuffix = "binaryCompatibility/" + protected lateinit var workingDir: File + + fun doTest(filePath: String) { + workingDir = File(pathToRootOutputDir + "out/$testGroupSuffix" + filePath) + workingDir.mkdirs() + doTestWithIgnoringByFailFile(filePath) + } + + fun doTestWithIgnoringByFailFile(filePath: String) { + val failFile = File("$filePath.fail") + try { + doTest(filePath, "OK") + } catch (e: Throwable) { + if (failFile.exists()) { + KotlinTestUtils.assertEqualsToFile(failFile, e.message ?: "") + } else { + throw e + } + } + } + + fun doTest(filePath: String, expectedResult: String) { + val file = File(filePath) + val fileContent = KtTestUtil.doLoadFile(file) + + val inputFiles = TestFiles.createTestFiles( + file.name, + fileContent, + object : TestFiles.TestFileFactory { + override fun createFile(module: TestModule?, fileName: String, text: String, directives: Directives) = + module?.let { + TestFile(module, fileName, text, directives) + } ?: error("Expected a module for $fileName in $filePath") + + override fun createModule(name: String, dependencies: List, friends: List) = + TestModule(name, dependencies, friends) + + }, + true, + true + ) + val modules = inputFiles + .map { it.module }.distinct() + .map { it.name to it }.toMap() + + val orderedModules = DFS.topologicalOrder(modules.values) { module -> + module.dependenciesSymbols.mapNotNull { modules[it] } + } + + val mainModuleName = DEFAULT_MODULE + val mainModule = modules[mainModuleName] ?: error("No module with name \"$mainModuleName\"") + + orderedModules.reversed().filterNot { it === mainModule }.forEach { + produceKlib(it, 1) + if (it.hasVersions) { + produceKlib(it, 2) + } + } + produceProgram(mainModule) + + runProgram(mainModule, expectedResult) + } + + abstract fun produceKlib(module: TestModule, version: Int) + abstract fun produceProgram(module: TestModule) + abstract fun runProgram(module: TestModule, expectedResult: String) + + companion object { + val TEST_FUNCTION = "box" + val DEFAULT_MODULE = "main" + } +} diff --git a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt index 0042c3b6ee6..de2b7e14f67 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt @@ -8,7 +8,11 @@ package org.jetbrains.kotlin.generators.tests import org.jetbrains.kotlin.generators.impl.generateTestGroupSuite import org.jetbrains.kotlin.js.test.AbstractDceTest import org.jetbrains.kotlin.js.test.AbstractJsLineNumberTest -import org.jetbrains.kotlin.js.test.es6.semantics.* +import org.jetbrains.kotlin.js.test.compatibility.binary.AbstractJsKlibBinaryCompatibilityTest +import org.jetbrains.kotlin.js.test.es6.semantics.AbstractIrBoxJsES6Test +import org.jetbrains.kotlin.js.test.es6.semantics.AbstractIrJsCodegenBoxES6Test +import org.jetbrains.kotlin.js.test.es6.semantics.AbstractIrJsCodegenInlineES6Test +import org.jetbrains.kotlin.js.test.es6.semantics.AbstractIrJsTypeScriptExportES6Test import org.jetbrains.kotlin.js.test.ir.semantics.* import org.jetbrains.kotlin.js.test.semantics.* import org.jetbrains.kotlin.js.test.wasm.semantics.AbstractIrCodegenBoxWasmTest @@ -124,5 +128,11 @@ fun main(args: Array) { model("codegen/box/arrays", targetBackend = TargetBackend.JS) } } + + testGroup("js/js.tests/tests-gen", "compiler/testData/binaryCompatibility", testRunnerMethodName = "runTest0") { + testClass { + model("klibEvolution", targetBackend = TargetBackend.JS_IR) + } + } } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/compatibility/binary/AbstractJsKlibBinaryCompatibilityTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/compatibility/binary/AbstractJsKlibBinaryCompatibilityTest.kt new file mode 100644 index 00000000000..7aba2083ece --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/compatibility/binary/AbstractJsKlibBinaryCompatibilityTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2021 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.compatibility.binary + +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.js.K2JSCompiler +import org.jetbrains.kotlin.compatibility.binary.* +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.js.test.V8JsTestChecker +import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION +import java.io.File + +abstract class AbstractJsKlibBinaryCompatibilityTest : AbstractKlibBinaryCompatibilityTest() { + + override fun createEnvironment() = + KotlinCoreEnvironment.createForTests(testRootDisposable, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES) + + private fun TestModule.name(version: Int) = if (this.hasVersions) "version$version/${this.name}" else this.name + + private fun List.toLibrariesArg(version: Int): String { + val fileNames = this.map { it.name(version) } + val allDependencies = fileNames.map { File(workingDir, it.klib).absolutePath } + STDLIB_DEPENDENCY + return allDependencies.joinToString(File.pathSeparator) + } + + private fun TestModule.dependenciesToLibrariesArg(version: Int): String = + this.dependencies.map { it as? TestModule ?: error("Unexpected dependency kind: $it") }.toLibrariesArg(version) + + private val TestModule.jsPath get() = File(workingDir, "${this.name}.js").absolutePath + + private fun createFiles(files: List): List = + files.map { + val file = File(workingDir, it.name) + file.writeText(it.content) + file.absolutePath + } + + private fun runnerFunctionFile(): String { + val file = File(workingDir, RUNNER_FUNCTION_FILE) + file.writeText(runnerFileText) + return file.absolutePath + } + + override fun produceKlib(module: TestModule, version: Int) { + val args = K2JSCompilerArguments().apply { + freeArgs = createFiles(module.versionFiles(version)) + libraries = module.dependenciesToLibrariesArg(version = version) + outputFile = File(workingDir, "${module.name(version)}.$KLIB_FILE_EXTENSION").absolutePath + irProduceKlibFile = true + irOnly = true + irModuleName = module.name + repositries = "$workingDir${File.pathSeparator}$workingDir/version$version" + } + K2JSCompiler().exec(TestMessageCollector(), Services.EMPTY, args) + } + + override fun produceProgram(module: TestModule) { + assert(!module.hasVersions) + val args = K2JSCompilerArguments().apply { + freeArgs = createFiles(module.files) + runnerFunctionFile() + libraries = module.dependenciesToLibrariesArg(version = 2) + outputFile = File(workingDir, module.name.js).absolutePath + irProduceJs = true + irOnly = true + irModuleName = module.name + repositries = "$workingDir${File.pathSeparator}$workingDir/version2" + } + K2JSCompiler().exec(TestMessageCollector(), Services.EMPTY, args) + } + + override fun runProgram(module: TestModule, expectedResult: String) { + testChecker.check(listOf(module.jsPath), module.name, null, RUNNER_FUNCTION, expectedResult, false) + } + + // TODO: ask js folks what to use here. + protected open val testChecker get() = V8JsTestChecker + + companion object { + private val String.klib: String get() = "$this.$KLIB_FILE_EXTENSION" + private val String.js: String get() = "$this.js" + + private val STDLIB_DEPENDENCY = System.getProperty("kotlin.js.full.stdlib.path") + + // A @JsExport wrapper for box(). + // Otherwise box() is not available in js. + private const val RUNNER_FUNCTION = "__js_exported_wrapper_function" + private const val RUNNER_FUNCTION_FILE = "js_exported_wrapper_function.kt" + private val runnerFileText = """ + @JsExport + fun $RUNNER_FUNCTION() = $TEST_FUNCTION() + """ + } +} + +abstract class AbstractJsKlibBinaryCompatibilityErrorTest : AbstractJsKlibBinaryCompatibilityTest() + +private class TestMessageCollector : MessageCollector { + override fun clear() {} + override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) { + if (severity.isError()) error(message) + } + override fun hasErrors(): Boolean = error("Unsupported operation") +} diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/compatibility/binary/JsKlibBinaryCompatibilityTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/compatibility/binary/JsKlibBinaryCompatibilityTestGenerated.java new file mode 100644 index 00000000000..4a7bac2c5ad --- /dev/null +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/compatibility/binary/JsKlibBinaryCompatibilityTestGenerated.java @@ -0,0 +1,222 @@ +/* + * Copyright 2010-2021 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.compatibility.binary; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/binaryCompatibility/klibEvolution") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class JsKlibBinaryCompatibilityTestGenerated extends AbstractJsKlibBinaryCompatibilityTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + @TestMetadata("addAbstractMemberBody.kt") + public void testAddAbstractMemberBody() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addAbstractMemberBody.kt"); + } + + @TestMetadata("addCompanionObject.kt") + public void testAddCompanionObject() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addCompanionObject.kt"); + } + + @TestMetadata("addDefaultImplementations.kt") + public void testAddDefaultImplementations() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addDefaultImplementations.kt"); + } + + @TestMetadata("addEnumClassMember.kt") + public void testAddEnumClassMember() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addEnumClassMember.kt"); + } + + @TestMetadata("addLateinitToVar.kt") + public void testAddLateinitToVar() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addLateinitToVar.kt"); + } + + @TestMetadata("addOpenToClass.kt") + public void testAddOpenToClass() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addOpenToClass.kt"); + } + + @TestMetadata("addOpenToMember.kt") + public void testAddOpenToMember() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addOpenToMember.kt"); + } + + @TestMetadata("addOrRemoveConst.kt") + public void testAddOrRemoveConst() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addOrRemoveConst.kt"); + } + + @TestMetadata("addOrRemoveInitBlock.kt") + public void testAddOrRemoveInitBlock() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addOrRemoveInitBlock.kt"); + } + + @TestMetadata("addOverloads.kt") + public void testAddOverloads() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addOverloads.kt"); + } + + @TestMetadata("addParameterDefaulValue.kt") + public void testAddParameterDefaulValue() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addParameterDefaulValue.kt"); + } + + @TestMetadata("addProeprtyAccessor.kt") + public void testAddProeprtyAccessor() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addProeprtyAccessor.kt"); + } + + @TestMetadata("addingSealedClassMember.kt") + public void testAddingSealedClassMember() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/addingSealedClassMember.kt"); + } + + public void testAllFilesPresentInKlibEvolution() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/binaryCompatibility/klibEvolution"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + + @TestMetadata("changeBaseClassOrder.kt") + public void testChangeBaseClassOrder() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changeBaseClassOrder.kt"); + } + + @TestMetadata("changeCompanionToNestedObject.kt") + public void testChangeCompanionToNestedObject() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changeCompanionToNestedObject.kt"); + } + + @TestMetadata("changeConstInitialization.kt") + public void testChangeConstInitialization() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changeConstInitialization.kt"); + } + + @TestMetadata("changeNamesOfTypeParameters.kt") + public void testChangeNamesOfTypeParameters() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changeNamesOfTypeParameters.kt"); + } + + @TestMetadata("changeObjectToCompanion.kt") + public void testChangeObjectToCompanion() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changeObjectToCompanion.kt"); + } + + @TestMetadata("changeParameterDefaultValue.kt") + public void testChangeParameterDefaultValue() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changeParameterDefaultValue.kt"); + } + + @TestMetadata("changePropertyFromValToVar.kt") + public void testChangePropertyFromValToVar() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changePropertyFromValToVar.kt"); + } + + @TestMetadata("changePropertyInitialization.kt") + public void testChangePropertyInitialization() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/changePropertyInitialization.kt"); + } + + @TestMetadata("constructorParameterMarkValVar.kt") + public void testConstructorParameterMarkValVar() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/constructorParameterMarkValVar.kt"); + } + + @TestMetadata("deleteOverrideMember.kt") + public void testDeleteOverrideMember() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/deleteOverrideMember.kt"); + } + + @TestMetadata("deletePrivateMembers.kt") + public void testDeletePrivateMembers() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/deletePrivateMembers.kt"); + } + + @TestMetadata("inlineBodyChange.kt") + public void testInlineBodyChange() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/inlineBodyChange.kt"); + } + + @TestMetadata("inlineFunction.kt") + public void testInlineFunction() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/inlineFunction.kt"); + } + + @TestMetadata("makeFunctionInfixOrTailrec.kt") + public void testMakeFunctionInfixOrTailrec() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/makeFunctionInfixOrTailrec.kt"); + } + + @TestMetadata("moreSpecificBaseClass.kt") + public void testMoreSpecificBaseClass() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/moreSpecificBaseClass.kt"); + } + + @TestMetadata("moveMemberUpInHierarchy.kt") + public void testMoveMemberUpInHierarchy() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/moveMemberUpInHierarchy.kt"); + } + + @TestMetadata("newOverrideMember.kt") + public void testNewOverrideMember() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/newOverrideMember.kt"); + } + + @TestMetadata("removeAbstractFromClass.kt") + public void testRemoveAbstractFromClass() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/removeAbstractFromClass.kt"); + } + + @TestMetadata("removeInfixOrTailrecFromFunction.kt") + public void testRemoveInfixOrTailrecFromFunction() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/removeInfixOrTailrecFromFunction.kt"); + } + + @TestMetadata("removeLateinitFromVar.kt") + public void testRemoveLateinitFromVar() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/removeLateinitFromVar.kt"); + } + + @TestMetadata("removePropertyAccessor.kt") + public void testRemovePropertyAccessor() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/removePropertyAccessor.kt"); + } + + @TestMetadata("renameArguments.kt") + public void testRenameArguments() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/renameArguments.kt"); + } + + @TestMetadata("reorderClassConstructors.kt") + public void testReorderClassConstructors() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/reorderClassConstructors.kt"); + } + + @TestMetadata("turnClassIntoDataClass.kt") + public void testTurnClassIntoDataClass() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/turnClassIntoDataClass.kt"); + } + + @TestMetadata("widenSuperMemberVisibility.kt") + public void testWidenSuperMemberVisibility() throws Exception { + runTest("compiler/testData/binaryCompatibility/klibEvolution/widenSuperMemberVisibility.kt"); + } +}