From 8ffde5fa69815c26e77a3cebb04e2f388d53e114 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Mon, 15 May 2017 14:17:10 +0300 Subject: [PATCH] Implement JS proto comparison Original commit: ed5b6e07aad9cce78e91de0635f089279b7a1626 --- jps/jps-plugin/jps-tests/jps-tests.iml | 3 + .../AbstractJsProtoComparisonTest.kt | 97 ++++++++ .../AbstractJvmProtoComparisonTest.kt | 3 +- .../AbstractProtoComparisonTest.kt | 9 +- .../JsProtoComparisonTestGenerated.java | 221 ++++++++++++++++++ .../classWitnEnumChanged/js.result.out | 4 + 6 files changed, 332 insertions(+), 5 deletions(-) create mode 100644 jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJsProtoComparisonTest.kt create mode 100644 jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/JsProtoComparisonTestGenerated.java create mode 100644 jps/jps-plugin/testData/comparison/classMembersOnlyChanged/classWitnEnumChanged/js.result.out diff --git a/jps/jps-plugin/jps-tests/jps-tests.iml b/jps/jps-plugin/jps-tests/jps-tests.iml index 3363179a26b..b4cb296a6a6 100644 --- a/jps/jps-plugin/jps-tests/jps-tests.iml +++ b/jps/jps-plugin/jps-tests/jps-tests.iml @@ -20,6 +20,9 @@ + + + \ No newline at end of file diff --git a/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJsProtoComparisonTest.kt b/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJsProtoComparisonTest.kt new file mode 100644 index 00000000000..ec8f4f73c5f --- /dev/null +++ b/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJsProtoComparisonTest.kt @@ -0,0 +1,97 @@ +/* + * 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.jps.incremental + +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants +import org.jetbrains.kotlin.cli.js.K2JSCompiler +import org.jetbrains.kotlin.config.Services +import org.jetbrains.kotlin.incremental.ClassProtoData +import org.jetbrains.kotlin.incremental.Difference +import org.jetbrains.kotlin.incremental.difference as differenceImpl +import org.jetbrains.kotlin.incremental.PackagePartProtoData +import org.jetbrains.kotlin.incremental.ProtoData +import org.jetbrains.kotlin.incremental.utils.TestMessageCollector +import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer +import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl +import org.jetbrains.kotlin.serialization.js.JsProtoBuf +import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol +import org.junit.Assert +import java.io.File + +abstract class AbstractJsProtoComparisonTest : AbstractProtoComparisonTest() { + override fun expectedOutputFile(testDir: File): File = + File(testDir, "js.result.out") + .takeIf { it.exists() } + ?: super.expectedOutputFile(testDir) + + override fun compileAndGetClasses(sourceDir: File, outputDir: File): Map { + val incrementalResults = IncrementalResultsConsumerImpl() + // todo: find out if it is safe to call directly + val services = Services.Builder().run { + register(IncrementalResultsConsumer::class.java, incrementalResults) + build() + } + + val ktFiles = sourceDir.walkMatching { it.name.endsWith(".kt") }.map { it.canonicalPath }.toList() + val messageCollector = TestMessageCollector() + val args = K2JSCompilerArguments().apply { + outputFile = File(outputDir, "out.js").canonicalPath + metaInfo = true + main = K2JsArgumentConstants.NO_CALL + freeArgs.addAll(ktFiles) + } + + K2JSCompiler().exec(messageCollector, services, args).let { exitCode -> + val expectedOutput = "OK" + val actualOutput = (listOf(exitCode.name) + messageCollector.errors).joinToString("\n") + Assert.assertEquals(expectedOutput, actualOutput) + } + + val classes = hashMapOf() + + for ((sourceFile, protoBytes, _) in incrementalResults.packageParts) { + val proto = ProtoBuf.PackageFragment.parseFrom(protoBytes, JsSerializerProtocol.extensionRegistry) + val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames) + + proto.class_List.forEach { + val classId = nameResolver.getClassId(it.fqName) + classes[classId] = ClassProtoData(it, nameResolver) + } + + proto.`package`.apply { + val packageFqName = if (hasExtension(JsProtoBuf.packageFqName)) { + nameResolver.getPackageFqName(getExtension(JsProtoBuf.packageFqName)) + } + else FqName.ROOT + + val packagePartClassId = ClassId(packageFqName, Name.identifier(sourceFile.nameWithoutExtension.capitalize() + "Kt")) + classes[packagePartClassId] = PackagePartProtoData(this, nameResolver) + } + } + + return classes + } + + override fun difference(oldData: ProtoData, newData: ProtoData): Difference? = + differenceImpl(oldData, newData) +} diff --git a/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJvmProtoComparisonTest.kt b/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJvmProtoComparisonTest.kt index 36ce3fc2eba..f14ea22eab1 100644 --- a/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJvmProtoComparisonTest.kt +++ b/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/AbstractJvmProtoComparisonTest.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.incremental.Difference import org.jetbrains.kotlin.incremental.LocalFileKotlinClass import org.jetbrains.kotlin.incremental.difference import org.jetbrains.kotlin.incremental.storage.ProtoMapValue -import org.jetbrains.kotlin.incremental.testingUtils.copyTestSources import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.ClassId @@ -40,7 +39,7 @@ abstract class AbstractJvmProtoComparisonTest : AbstractProtoComparisonTest : UsefulTestCase() { +abstract class AbstractProtoComparisonTest : TestWithWorkingDir() { protected abstract fun compileAndGetClasses(sourceDir: File, outputDir: File): Map protected abstract fun difference(oldData: PROTO_DATA, newData: PROTO_DATA): Difference? + protected open fun expectedOutputFile(testDir: File): File = + File(testDir, "result.out") + fun doTest(testDataPath: String) { val testDir = File(testDataPath) - val workingDir = KotlinTestUtils.tmpDir("testDirectory") val oldClassMap = classesForPrefixedSources(testDir, workingDir, "old") val newClassMap = classesForPrefixedSources(testDir, workingDir, "new") @@ -69,7 +72,7 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() { p.println("CHANGES in $classId: ${changes.joinToString()}") } - KotlinTestUtils.assertEqualsToFile(File(testDataPath + File.separator + "result.out"), sb.toString()) + KotlinTestUtils.assertEqualsToFile(expectedOutputFile(testDir), sb.toString()) } private fun classesForPrefixedSources(testDir: File, workingDir: File, prefix: String): Map { diff --git a/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/JsProtoComparisonTestGenerated.java b/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/JsProtoComparisonTestGenerated.java new file mode 100644 index 00000000000..57af389c73c --- /dev/null +++ b/jps/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/incremental/JsProtoComparisonTestGenerated.java @@ -0,0 +1,221 @@ +/* + * 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.jps.incremental; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +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") +@RunWith(JUnit3RunnerWithInners.class) +public class JsProtoComparisonTestGenerated extends AbstractJsProtoComparisonTest { + @TestMetadata("jps-plugin/testData/comparison/classSignatureChange") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ClassSignatureChange extends AbstractJsProtoComparisonTest { + public void testAllFilesPresentInClassSignatureChange() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/comparison/classSignatureChange"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("classAnnotationListChanged") + public void testClassAnnotationListChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classAnnotationListChanged/"); + doTest(fileName); + } + + @TestMetadata("classFlagsAndMembersChanged") + public void testClassFlagsAndMembersChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classFlagsAndMembersChanged/"); + doTest(fileName); + } + + @TestMetadata("classFlagsChanged") + public void testClassFlagsChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classFlagsChanged/"); + doTest(fileName); + } + + @TestMetadata("classTypeParameterListChanged") + public void testClassTypeParameterListChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classTypeParameterListChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithSuperTypeListChanged") + public void testClassWithSuperTypeListChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classSignatureChange/classWithSuperTypeListChanged/"); + doTest(fileName); + } + } + + @TestMetadata("jps-plugin/testData/comparison/classPrivateOnlyChange") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ClassPrivateOnlyChange extends AbstractJsProtoComparisonTest { + public void testAllFilesPresentInClassPrivateOnlyChange() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/comparison/classPrivateOnlyChange"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("classWithPrivateFunChanged") + public void testClassWithPrivateFunChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classPrivateOnlyChange/classWithPrivateFunChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithPrivatePrimaryConstructorChanged") + public void testClassWithPrivatePrimaryConstructorChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classPrivateOnlyChange/classWithPrivatePrimaryConstructorChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithPrivateSecondaryConstructorChanged") + public void testClassWithPrivateSecondaryConstructorChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classPrivateOnlyChange/classWithPrivateSecondaryConstructorChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithPrivateValChanged") + public void testClassWithPrivateValChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classPrivateOnlyChange/classWithPrivateValChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithPrivateVarChanged") + public void testClassWithPrivateVarChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classPrivateOnlyChange/classWithPrivateVarChanged/"); + doTest(fileName); + } + } + + @TestMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ClassMembersOnlyChanged extends AbstractJsProtoComparisonTest { + public void testAllFilesPresentInClassMembersOnlyChanged() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/comparison/classMembersOnlyChanged"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("classWithCompanionObjectChanged") + public void testClassWithCompanionObjectChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/classWithCompanionObjectChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithConstructorChanged") + public void testClassWithConstructorChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/classWithConstructorChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithFunAndValChanged") + public void testClassWithFunAndValChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/classWithFunAndValChanged/"); + doTest(fileName); + } + + @TestMetadata("classWithNestedClassesChanged") + public void testClassWithNestedClassesChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/classWithNestedClassesChanged/"); + doTest(fileName); + } + + @TestMetadata("classWitnEnumChanged") + public void testClassWitnEnumChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/classWitnEnumChanged/"); + doTest(fileName); + } + + @TestMetadata("defaultValues") + public void testDefaultValues() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/defaultValues/"); + doTest(fileName); + } + + @TestMetadata("membersFlagsChanged") + public void testMembersFlagsChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/membersFlagsChanged/"); + doTest(fileName); + } + + @TestMetadata("sealedClassImplAdded") + public void testSealedClassImplAdded() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/classMembersOnlyChanged/sealedClassImplAdded/"); + doTest(fileName); + } + } + + @TestMetadata("jps-plugin/testData/comparison/packageMembers") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PackageMembers extends AbstractJsProtoComparisonTest { + public void testAllFilesPresentInPackageMembers() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/comparison/packageMembers"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("defaultValues") + public void testDefaultValues() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/packageMembers/defaultValues/"); + doTest(fileName); + } + + @TestMetadata("membersFlagsChanged") + public void testMembersFlagsChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/packageMembers/membersFlagsChanged/"); + doTest(fileName); + } + + @TestMetadata("packageFacadePrivateOnlyChanges") + public void testPackageFacadePrivateOnlyChanges() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/packageMembers/packageFacadePrivateOnlyChanges/"); + doTest(fileName); + } + + @TestMetadata("packageFacadePublicChanges") + public void testPackageFacadePublicChanges() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/packageMembers/packageFacadePublicChanges/"); + doTest(fileName); + } + } + + @TestMetadata("jps-plugin/testData/comparison/unchanged") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unchanged extends AbstractJsProtoComparisonTest { + public void testAllFilesPresentInUnchanged() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/comparison/unchanged"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("unchangedClass") + public void testUnchangedClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/unchanged/unchangedClass/"); + doTest(fileName); + } + + @TestMetadata("unchangedPackageFacade") + public void testUnchangedPackageFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/comparison/unchanged/unchangedPackageFacade/"); + doTest(fileName); + } + } +} diff --git a/jps/jps-plugin/testData/comparison/classMembersOnlyChanged/classWitnEnumChanged/js.result.out b/jps/jps-plugin/testData/comparison/classMembersOnlyChanged/classWitnEnumChanged/js.result.out new file mode 100644 index 00000000000..add71453244 --- /dev/null +++ b/jps/jps-plugin/testData/comparison/classMembersOnlyChanged/classWitnEnumChanged/js.result.out @@ -0,0 +1,4 @@ +REMOVED test/EnumClassWithChanges.CONST_2 +ADDED test/EnumClassWithChanges.CONST_3 +ADDED test/EnumClassWithChanges.CONST_4 +CHANGES in test/EnumClassWithChanges: CLASS_SIGNATURE