From 37cfb2fcc09a766d693e1573c6aea5a76663a24d Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Tue, 20 Nov 2012 20:34:01 +0400 Subject: [PATCH] Checking errors when loading from Java in tests. --- .../AbstractLoadCompiledKotlinTest.java | 4 +- .../jvm/compiler/AbstractLoadJavaTest.java | 13 +- .../jvm/compiler/ExpectedLoadErrorsUtil.java | 129 ++++++++++++++++++ .../jet/jvm/compiler/LoadDescriptorUtil.java | 15 +- .../jet/jvm/compiler/LoadJavaCustomTest.java | 24 ++-- .../compiler/annotation/ExpectLoadError.java | 12 ++ .../jet/test/util/NamespaceComparator.java | 26 +++- 7 files changed, 200 insertions(+), 23 deletions(-) create mode 100644 compiler/tests/org/jetbrains/jet/jvm/compiler/ExpectedLoadErrorsUtil.java create mode 100644 compiler/tests/org/jetbrains/jet/jvm/compiler/annotation/ExpectLoadError.java diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadCompiledKotlinTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadCompiledKotlinTest.java index fad910ffd29..ac2f3344acd 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadCompiledKotlinTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadCompiledKotlinTest.java @@ -48,8 +48,8 @@ public abstract class AbstractLoadCompiledKotlinTest extends TestCaseWithTmpdir TEST_PACKAGE_FQNAME); assert namespaceFromSource != null; Assert.assertEquals("test", namespaceFromSource.getName().getName()); - NamespaceDescriptor namespaceFromClass = LoadDescriptorUtil.loadTestNamespaceFromBinaries(tmpdir, getTestRootDisposable(), - ConfigurationKind.JDK_ONLY); + NamespaceDescriptor namespaceFromClass = LoadDescriptorUtil.loadTestNamespaceAndBindingContextFromBinaries( + tmpdir, getTestRootDisposable(), ConfigurationKind.JDK_ONLY).first; compareNamespaces(namespaceFromSource, namespaceFromClass, NamespaceComparator.DONT_INCLUDE_METHODS_OF_OBJECT, txtFile); } } diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadJavaTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadJavaTest.java index 97f0668e0c6..749e4d2be80 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadJavaTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractLoadJavaTest.java @@ -16,17 +16,19 @@ package org.jetbrains.jet.jvm.compiler; +import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.ConfigurationKind; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.test.TestCaseWithTmpdir; import org.junit.Assert; import java.io.File; -import java.util.Collections; +import java.util.Arrays; import static org.jetbrains.jet.jvm.compiler.LoadDescriptorUtil.analyzeKotlinAndLoadTestNamespace; -import static org.jetbrains.jet.jvm.compiler.LoadDescriptorUtil.compileJavaAndLoadTestNamespaceFromBinary; +import static org.jetbrains.jet.jvm.compiler.LoadDescriptorUtil.compileJavaAndLoadTestNamespaceAndBindingContextFromBinary; import static org.jetbrains.jet.test.util.NamespaceComparator.DONT_INCLUDE_METHODS_OF_OBJECT; import static org.jetbrains.jet.test.util.NamespaceComparator.compareNamespaces; @@ -44,8 +46,11 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir { File ktFile = new File(javaFile.getPath().replaceFirst("\\.java$", ".kt")); File txtFile = new File(javaFile.getPath().replaceFirst("\\.java$", ".txt")); NamespaceDescriptor nsa = analyzeKotlinAndLoadTestNamespace(ktFile, myTestRootDisposable, ConfigurationKind.JDK_AND_ANNOTATIONS); - NamespaceDescriptor nsb = compileJavaAndLoadTestNamespaceFromBinary(Collections.singletonList(javaFile), - tmpdir, myTestRootDisposable, ConfigurationKind.JDK_AND_ANNOTATIONS); + Pair nsbAndBindingContext = compileJavaAndLoadTestNamespaceAndBindingContextFromBinary( + Arrays.asList(javaFile, ExpectedLoadErrorsUtil.ANNOTATION_SOURCE_FILE), + tmpdir, myTestRootDisposable, ConfigurationKind.JDK_AND_ANNOTATIONS); + NamespaceDescriptor nsb = nsbAndBindingContext.first; + ExpectedLoadErrorsUtil.checkForLoadErrors(nsb, nsbAndBindingContext.second); compareNamespaces(nsa, nsb, DONT_INCLUDE_METHODS_OF_OBJECT, txtFile); } } diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/ExpectedLoadErrorsUtil.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/ExpectedLoadErrorsUtil.java new file mode 100644 index 00000000000..5522a748eb6 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/ExpectedLoadErrorsUtil.java @@ -0,0 +1,129 @@ +/* + * Copyright 2010-2012 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.jet.jvm.compiler; + +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.jvm.compiler.annotation.ExpectLoadError; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.resolve.DescriptorRenderer; + +import java.io.File; +import java.util.*; + +import static com.intellij.testFramework.UsefulTestCase.assertNotNull; +import static com.intellij.testFramework.UsefulTestCase.assertSameElements; + +public class ExpectedLoadErrorsUtil { + public static final String ANNOTATION_CLASS_NAME = ExpectLoadError.class.getName(); + public static final File ANNOTATION_SOURCE_FILE = new File("compiler/tests/" + ANNOTATION_CLASS_NAME.replace('.', '/') + ".java"); + + public static void checkForLoadErrors( + @NotNull NamespaceDescriptor namespaceFromJava, + @NotNull BindingContext bindingContext + ) { + Map> expectedErrors = getExpectedLoadErrors(namespaceFromJava); + Map> actualErrors = getActualLoadErrors(bindingContext); + + for (DeclarationDescriptor descriptor : ContainerUtil.union(expectedErrors.keySet(), actualErrors.keySet())) { + List actual = actualErrors.get(descriptor); + List expected = expectedErrors.get(descriptor); + String rendered = DescriptorRenderer.TEXT.render(descriptor); + + assertNotNull("Unexpected load error(s):\n" + actual + "\ncontainer:" + rendered, expected); + assertNotNull("Missing load error(s):\n" + expected + "\ncontainer:" + rendered, actual); + + assertSameElements("Unexpected/missing load error(s)\ncontainer:" + rendered, actual, expected); + } + } + + private static Map> getExpectedLoadErrors(@NotNull NamespaceDescriptor namespaceFromJava) { + final Map> map = new HashMap>(); + + namespaceFromJava.acceptVoid(new DeclarationDescriptorVisitorEmptyBodies() { + @Override + public Void visitNamespaceDescriptor(NamespaceDescriptor descriptor, Void data) { + return visitDeclarationRecursively(descriptor, descriptor.getMemberScope()); + } + + @Override + public Void visitClassDescriptor(ClassDescriptor descriptor, Void data) { + return visitDeclarationRecursively(descriptor, descriptor.getDefaultType().getMemberScope()); + } + + @Override + public Void visitFunctionDescriptor(FunctionDescriptor descriptor, Void data) { + return visitDeclaration(descriptor); + } + + @Override + public Void visitPropertyDescriptor(PropertyDescriptor descriptor, Void data) { + return visitDeclaration(descriptor); + } + + private Void visitDeclaration(@NotNull DeclarationDescriptor descriptor) { + for (AnnotationDescriptor annotation : descriptor.getAnnotations()) { + ClassDescriptor annotationClass = (ClassDescriptor) annotation.getType().getConstructor().getDeclarationDescriptor(); + assert annotationClass != null; + + if (DescriptorUtils.getFQName(annotationClass).getFqName().equals(ANNOTATION_CLASS_NAME)) { + + // we expect exactly one annotation argument + CompileTimeConstant argument = annotation.getAllValueArguments().values().iterator().next(); + + String error = (String) argument.getValue(); + List errors = Arrays.asList(error.split("\\|")); + + map.put(descriptor, errors); + } + } + + return null; + } + + private Void visitDeclarationRecursively(@NotNull DeclarationDescriptor descriptor, @NotNull JetScope memberScope) { + for (DeclarationDescriptor member : memberScope.getAllDescriptors()) { + member.acceptVoid(this); + } + + return visitDeclaration(descriptor); + } + }); + + return map; + } + + private static Map> getActualLoadErrors(@NotNull BindingContext bindingContext) { + Map> result = new HashMap>(); + + Collection descriptors = bindingContext.getKeys(BindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS); + for (DeclarationDescriptor descriptor : descriptors) { + List errors = bindingContext.get(BindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor); + result.put(descriptor, errors); + } + + return result; + } + + private ExpectedLoadErrorsUtil() { + } +} diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadDescriptorUtil.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadDescriptorUtil.java index 75fa70770ae..518e3abc889 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadDescriptorUtil.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadDescriptorUtil.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.jvm.compiler; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.CompileCompilerDependenciesTest; @@ -70,7 +71,7 @@ public final class LoadDescriptorUtil { ) throws IOException { compileKotlinToDirAndGetAnalyzeExhaust(kotlinFile, outDir, disposable, configurationKind); - return loadTestNamespaceFromBinaries(outDir, disposable, ConfigurationKind.JDK_ONLY); + return loadTestNamespaceAndBindingContextFromBinaries(outDir, disposable, ConfigurationKind.JDK_ONLY).first; } @NotNull @@ -89,7 +90,11 @@ public final class LoadDescriptorUtil { } @NotNull - public static NamespaceDescriptor loadTestNamespaceFromBinaries(@NotNull File outDir, @NotNull Disposable disposable, @NotNull ConfigurationKind configurationKind) { + public static Pair loadTestNamespaceAndBindingContextFromBinaries( + @NotNull File outDir, + @NotNull Disposable disposable, + @NotNull ConfigurationKind configurationKind + ) { Disposer.dispose(disposable); CompilerConfiguration configuration = CompileCompilerDependenciesTest.compilerConfigurationForTests( @@ -101,11 +106,11 @@ public final class LoadDescriptorUtil { NamespaceDescriptor namespaceDescriptor = javaDescriptorResolver.resolveNamespace(TEST_PACKAGE_FQNAME, DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN); assert namespaceDescriptor != null; - return namespaceDescriptor; + return Pair.create(namespaceDescriptor, injector.getBindingTrace().getBindingContext()); } @NotNull - public static NamespaceDescriptor compileJavaAndLoadTestNamespaceFromBinary( + public static Pair compileJavaAndLoadTestNamespaceAndBindingContextFromBinary( @NotNull Collection javaFiles, @NotNull File outDir, @NotNull Disposable disposable, @@ -113,7 +118,7 @@ public final class LoadDescriptorUtil { ) throws IOException { compileJavaWithAnnotationsJar(javaFiles, outDir); - return loadTestNamespaceFromBinaries(outDir, disposable, configurationKind); + return loadTestNamespaceAndBindingContextFromBinaries(outDir, disposable, configurationKind); } private static void compileJavaWithAnnotationsJar(@NotNull Collection javaFiles, @NotNull File outDir) throws IOException { diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaCustomTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaCustomTest.java index fa89abb2da6..c443efbbaa1 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaCustomTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaCustomTest.java @@ -16,21 +16,23 @@ package org.jetbrains.jet.jvm.compiler; -import com.google.common.base.Function; -import com.google.common.collect.Collections2; +import com.intellij.openapi.util.Pair; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.ConfigurationKind; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.lazy.KotlinTestWithEnvironment; import org.jetbrains.jet.test.util.NamespaceComparator; import java.io.File; import java.util.Arrays; -import java.util.Collection; +import java.util.List; -import static org.jetbrains.jet.jvm.compiler.LoadDescriptorUtil.compileJavaAndLoadTestNamespaceFromBinary; +import static org.jetbrains.jet.jvm.compiler.LoadDescriptorUtil.compileJavaAndLoadTestNamespaceAndBindingContextFromBinary; /** * @author Pavel Talanov @@ -48,17 +50,23 @@ public final class LoadJavaCustomTest extends KotlinTestWithEnvironment { } private void doTest(@NotNull String expectedFileName, @NotNull String... javaFileNames) throws Exception { - Collection files = Collections2.transform(Arrays.asList(javaFileNames), new Function() { + List files = ContainerUtil.map(Arrays.asList(javaFileNames), new Function() { @Override - public File apply(String s) { + public File fun(String s) { return new File(s); } }); + files.add(ExpectedLoadErrorsUtil.ANNOTATION_SOURCE_FILE); File expected = new File(expectedFileName); File tmpDir = JetTestUtils.tmpDir(expected.getName()); - NamespaceDescriptor javaNamespaceDescriptor = compileJavaAndLoadTestNamespaceFromBinary(files, tmpDir, getTestRootDisposable(), ConfigurationKind.JDK_ONLY); + + Pair javaNamespaceAndBindingContext + = compileJavaAndLoadTestNamespaceAndBindingContextFromBinary(files, tmpDir, getTestRootDisposable(), + ConfigurationKind.JDK_ONLY); + NamespaceDescriptor javaNamespace = javaNamespaceAndBindingContext.first; //NOTE: comparing namespace to file (hack) - NamespaceComparator.compareNamespaces(javaNamespaceDescriptor, javaNamespaceDescriptor, NamespaceComparator.DONT_INCLUDE_METHODS_OF_OBJECT, expected); + NamespaceComparator.compareNamespaces(javaNamespace, javaNamespace, NamespaceComparator.DONT_INCLUDE_METHODS_OF_OBJECT, expected); + ExpectedLoadErrorsUtil.checkForLoadErrors(javaNamespace, javaNamespaceAndBindingContext.second); } public void testPackageLocalVisibility() throws Exception { diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/annotation/ExpectLoadError.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/annotation/ExpectLoadError.java new file mode 100644 index 00000000000..187e4da58ac --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/annotation/ExpectLoadError.java @@ -0,0 +1,12 @@ +package org.jetbrains.jet.jvm.compiler.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +public @interface ExpectLoadError { + String value(); +} diff --git a/compiler/tests/org/jetbrains/jet/test/util/NamespaceComparator.java b/compiler/tests/org/jetbrains/jet/test/util/NamespaceComparator.java index cf36a57f71d..2525e43407d 100644 --- a/compiler/tests/org/jetbrains/jet/test/util/NamespaceComparator.java +++ b/compiler/tests/org/jetbrains/jet/test/util/NamespaceComparator.java @@ -21,9 +21,12 @@ import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.codegen.PropertyCodegen; +import org.jetbrains.jet.jvm.compiler.ExpectedLoadErrorsUtil; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.resolve.DescriptorUtils; @@ -382,14 +385,28 @@ public class NamespaceComparator { } } + // We want to skip @ExpectLoadErrors annotations when comparing + private static List filterAnnotations(List annotations) { + return ContainerUtil.filter(annotations, new Condition() { + @Override + public boolean value(AnnotationDescriptor annotation) { + ClassDescriptor annotationClass = (ClassDescriptor) annotation.getType().getConstructor().getDeclarationDescriptor(); + assert annotationClass != null; + + return !DescriptorUtils.getFQName(annotationClass).getFqName().equals(ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME); + } + }); + } + public void serialize(FunctionDescriptor fun) { serialize(fun.getVisibility()); sb.append(" "); serialize(fun.getModality()); sb.append(" "); - if (!fun.getAnnotations().isEmpty()) { - new Serializer(sb).serializeSeparated(fun.getAnnotations(), " "); + List annotations = filterAnnotations(fun.getAnnotations()); + if (!annotations.isEmpty()) { + new Serializer(sb).serializeSeparated(annotations, " "); sb.append(" "); } @@ -464,8 +481,9 @@ public class NamespaceComparator { serialize(prop.getModality()); sb.append(" "); - if (!prop.getAnnotations().isEmpty()) { - new Serializer(sb).serializeSeparated(prop.getAnnotations(), " "); + List annotations = filterAnnotations(prop.getAnnotations()); + if (!annotations.isEmpty()) { + new Serializer(sb).serializeSeparated(annotations, " "); sb.append(" "); }