/* * Copyright 2010-2015 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.jvm.compiler; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import junit.framework.ComparisonFailure; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.analyzer.AnalysisResult; import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport; import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt; import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.config.ContentRootsKt; import org.jetbrains.kotlin.config.JVMConfigurationKeys; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.descriptors.PackageViewDescriptor; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM; import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor; import org.jetbrains.kotlin.test.ConfigurationKind; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.TestCaseWithTmpdir; import org.jetbrains.kotlin.test.TestJdkKind; import org.jetbrains.kotlin.test.util.DescriptorValidator; import org.junit.Assert; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import static org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil.*; import static org.jetbrains.kotlin.test.KotlinTestUtils.*; import static org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesAllowed; import static org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden; import static org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.*; /* The generated test compares package descriptors loaded from kotlin sources and read from compiled java. */ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir { // There are two modules in each test case (sources and dependencies), so we should render declarations from both of them public static final Configuration COMPARATOR_CONFIGURATION = DONT_INCLUDE_METHODS_OF_OBJECT.renderDeclarationsFromOtherModules(true); protected void doTestCompiledJava(@NotNull String javaFileName) throws Exception { doTestCompiledJava(javaFileName, COMPARATOR_CONFIGURATION); } // Java-Kotlin dependencies are not supported in this method for simplicity protected void doTestCompiledJavaAndKotlin(@NotNull String expectedFileName) throws Exception { File expectedFile = new File(expectedFileName); File sourcesDir = new File(expectedFileName.replaceFirst("\\.txt$", "")); List<File> kotlinSources = FileUtil.findFilesByMask(Pattern.compile(".+\\.kt"), sourcesDir); KotlinCoreEnvironment environment = KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable, ConfigurationKind.JDK_ONLY); compileKotlinToDirAndGetModule(kotlinSources, tmpdir, environment); List<File> javaSources = FileUtil.findFilesByMask(Pattern.compile(".+\\.java"), sourcesDir); Pair<PackageViewDescriptor, BindingContext> binaryPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary( javaSources, tmpdir, ConfigurationKind.JDK_ONLY ); checkJavaPackage(expectedFile, binaryPackageAndContext.first, binaryPackageAndContext.second, COMPARATOR_CONFIGURATION); } protected void doTestCompiledJavaIncludeObjectMethods(@NotNull String javaFileName) throws Exception { doTestCompiledJava(javaFileName, RECURSIVE.renderDeclarationsFromOtherModules(true)); } protected void doTestCompiledKotlin(@NotNull String ktFileName) throws Exception { doTestCompiledKotlin(ktFileName, ConfigurationKind.JDK_ONLY, false); } protected void doTestCompiledKotlinWithTypeTable(@NotNull String ktFileName) throws Exception { doTestCompiledKotlin(ktFileName, ConfigurationKind.JDK_ONLY, true); } protected void doTestCompiledKotlinWithStdlib(@NotNull String ktFileName) throws Exception { doTestCompiledKotlin(ktFileName, ConfigurationKind.ALL, false); } private void doTestCompiledKotlin( @NotNull String ktFileName, @NotNull ConfigurationKind configurationKind, boolean useTypeTableInSerializer ) throws Exception { File ktFile = new File(ktFileName); File txtFile = new File(ktFileName.replaceFirst("\\.kt$", ".txt")); CompilerConfiguration configuration = newConfiguration(configurationKind, TestJdkKind.MOCK_JDK, getAnnotationsJar()); if (useTypeTableInSerializer) { configuration.put(JVMConfigurationKeys.USE_TYPE_TABLE, true); } KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForTests(getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES); ModuleDescriptor module = compileKotlinToDirAndGetModule(Collections.singletonList(ktFile), tmpdir, environment); PackageViewDescriptor packageFromSource = module.getPackage(TEST_PACKAGE_FQNAME); Assert.assertEquals("test", packageFromSource.getName().asString()); PackageViewDescriptor packageFromBinary = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot( tmpdir, getTestRootDisposable(), getJdkKind(), configurationKind, true, false ).first; for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(packageFromBinary.getMemberScope())) { if (descriptor instanceof ClassDescriptor) { assert descriptor instanceof DeserializedClassDescriptor : DescriptorUtils.getFqName(descriptor) + " is loaded as " + descriptor.getClass(); } } DescriptorValidator.validate(errorTypesForbidden(), packageFromSource); DescriptorValidator.validate(new DeserializedScopeValidationVisitor(), packageFromBinary); Configuration comparatorConfiguration = COMPARATOR_CONFIGURATION.checkPrimaryConstructors(true).checkPropertyAccessors(true); compareDescriptors(packageFromSource, packageFromBinary, comparatorConfiguration, txtFile); } protected boolean useFastClassFilesReading() { return false; } protected void doTestJavaAgainstKotlin(String expectedFileName) throws Exception { File expectedFile = new File(expectedFileName); File sourcesDir = new File(expectedFileName.replaceFirst("\\.txt$", "")); FileUtil.copyDir(sourcesDir, new File(tmpdir, "test"), pathname -> pathname.getName().endsWith(".java")); CompilerConfiguration configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.JDK_ONLY, getJdkKind()); ContentRootsKt.addKotlinSourceRoot(configuration, sourcesDir.getAbsolutePath()); JvmContentRootsKt.addJavaSourceRoot(configuration, new File("compiler/testData/loadJava/include")); JvmContentRootsKt.addJavaSourceRoot(configuration, tmpdir); KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForTests(getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES); AnalysisResult result = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( environment.getProject(), environment.getSourceFiles(), new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(), configuration, scope -> new JvmPackagePartProvider(environment, scope) ); PackageViewDescriptor packageView = result.getModuleDescriptor().getPackage(TEST_PACKAGE_FQNAME); checkJavaPackage(expectedFile, packageView, result.getBindingContext(), COMPARATOR_CONFIGURATION); } // TODO: add more tests on inherited parameter names, but currently impossible because of KT-4509 protected void doTestKotlinAgainstCompiledJavaWithKotlin(@NotNull String expectedFileName) throws Exception { File kotlinSrc = new File(expectedFileName); File librarySrc = new File(expectedFileName.replaceFirst("\\.kt$", "")); File expectedFile = new File(expectedFileName.replaceFirst("\\.kt$", ".txt")); File libraryOut = new File(tmpdir, "libraryOut"); compileKotlinWithJava( FileUtil.findFilesByMask(Pattern.compile(".+\\.java$"), librarySrc), FileUtil.findFilesByMask(Pattern.compile(".+\\.kt$"), librarySrc), libraryOut, getTestRootDisposable(), null ); KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForTests( getTestRootDisposable(), newConfiguration(ConfigurationKind.JDK_ONLY, getJdkKind(), getAnnotationsJar(), libraryOut), EnvironmentConfigFiles.JVM_CONFIG_FILES ); KtFile ktFile = KotlinTestUtils.createFile(kotlinSrc.getPath(), FileUtil.loadFile(kotlinSrc, true), environment.getProject()); ModuleDescriptor module = JvmResolveUtil.analyzeAndCheckForErrors(Collections.singleton(ktFile), environment).getModuleDescriptor(); PackageViewDescriptor packageView = module.getPackage(TEST_PACKAGE_FQNAME); assertFalse(packageView.isEmpty()); validateAndCompareDescriptorWithFile(packageView, COMPARATOR_CONFIGURATION.withValidationStrategy( new DeserializedScopeValidationVisitor() ), expectedFile); } @NotNull protected TestJdkKind getJdkKind() { return TestJdkKind.MOCK_JDK; } protected void doTestSourceJava(@NotNull String javaFileName) throws Exception { File originalJavaFile = new File(javaFileName); File expectedFile = getTxtFile(javaFileName); File testPackageDir = new File(tmpdir, "test"); assertTrue(testPackageDir.mkdir()); FileUtil.copy(originalJavaFile, new File(testPackageDir, originalJavaFile.getName())); Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = loadTestPackageAndBindingContextFromJavaRoot( tmpdir, getTestRootDisposable(), getJdkKind(), ConfigurationKind.JDK_ONLY, false, false); checkJavaPackage( expectedFile, javaPackageAndContext.first, javaPackageAndContext.second, COMPARATOR_CONFIGURATION.withValidationStrategy(errorTypesAllowed()) ); } private void doTestCompiledJava(@NotNull String javaFileName, Configuration configuration) throws Exception { File srcDir = new File(tmpdir, "src"); File compiledDir = new File(tmpdir, "compiled"); assertTrue(srcDir.mkdir()); assertTrue(compiledDir.mkdir()); List<File> srcFiles = KotlinTestUtils.createTestFiles( new File(javaFileName).getName(), FileUtil.loadFile(new File(javaFileName), true), new TestFileFactoryNoModules<File>() { @NotNull @Override public File create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) { File targetFile = new File(srcDir, fileName); try { FileUtil.writeToFile(targetFile, text); } catch (IOException e) { throw new AssertionError(e); } return targetFile; } }); Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary( srcFiles, compiledDir, ConfigurationKind.ALL ); checkJavaPackage(getTxtFile(javaFileName), javaPackageAndContext.first, javaPackageAndContext.second, configuration); } @NotNull private Pair<PackageViewDescriptor, BindingContext> compileJavaAndLoadTestPackageAndBindingContextFromBinary( @NotNull Collection<File> javaFiles, @NotNull File outDir, @NotNull ConfigurationKind configurationKind ) throws IOException { compileJavaWithAnnotationsJar(javaFiles, outDir); return loadTestPackageAndBindingContextFromJavaRoot(outDir, myTestRootDisposable, getJdkKind(), configurationKind, true, useFastClassFilesReading()); } private static void checkJavaPackage( File txtFile, PackageViewDescriptor javaPackage, BindingContext bindingContext, Configuration configuration ) { boolean fail = false; try { ExpectedLoadErrorsUtil.checkForLoadErrors(javaPackage, bindingContext); } catch (ComparisonFailure e) { // to let the next check run even if this one failed System.err.println("Expected: " + e.getExpected()); System.err.println("Actual : " + e.getActual()); e.printStackTrace(); fail = true; } catch (AssertionError e) { e.printStackTrace(); fail = true; } validateAndCompareDescriptorWithFile(javaPackage, configuration, txtFile); if (fail) { fail("See error above"); } } private static File getTxtFile(String javaFileName) { return new File(javaFileName.replaceFirst("\\.java$", ".txt")); } }