From c7f147d058def3afb8ed6479a4c905e8f8ad3617 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Fri, 17 Mar 2017 22:25:13 +0300 Subject: [PATCH] Introduce NavigationWithMultipleLibrariesTest Test navigation to library decompiled and source declaration when there are multiple copies of the same library in project Related to ba1ee99e97a3e1e28b471e9e06649fa0b2ddd3ed --- .../idea/caches/resolve/IdeaModuleInfos.kt | 2 +- .../expected.decompiled | 6 + .../navigationMultipleLibs/expected.sources | 7 ++ .../navigationMultipleLibs/libSrc/library.kt | 10 ++ .../navigationMultipleLibs/src/module.kt | 5 + .../NavigationWithMultipleLibrariesTest.kt | 115 ++++++++++++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 idea/testData/decompiler/navigationMultipleLibs/expected.decompiled create mode 100644 idea/testData/decompiler/navigationMultipleLibs/expected.sources create mode 100644 idea/testData/decompiler/navigationMultipleLibs/libSrc/library.kt create mode 100644 idea/testData/decompiler/navigationMultipleLibs/src/module.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/NavigationWithMultipleLibrariesTest.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt index 21d6c7be4b0..4dc46cae396 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt @@ -214,7 +214,7 @@ open class LibraryInfo(val project: Project, val library: Library) : IdeaModuleI override fun hashCode(): Int = 43 * library.hashCode() } -internal data class LibrarySourceInfo(val project: Project, val library: Library) : IdeaModuleInfo { +data class LibrarySourceInfo(val project: Project, val library: Library) : IdeaModuleInfo { override val moduleOrigin: ModuleOrigin get() = ModuleOrigin.OTHER diff --git a/idea/testData/decompiler/navigationMultipleLibs/expected.decompiled b/idea/testData/decompiler/navigationMultipleLibs/expected.decompiled new file mode 100644 index 00000000000..39df06420c3 --- /dev/null +++ b/idea/testData/decompiler/navigationMultipleLibs/expected.decompiled @@ -0,0 +1,6 @@ +A.class +public final class A public <3>constructor() { + LClass.class +public final class <1>LClass public constructor() { + LibraryKt.class +public fun lib.LClass.<2>f(a: lib.A): kotlin.Unit { /* compiled code */ } diff --git a/idea/testData/decompiler/navigationMultipleLibs/expected.sources b/idea/testData/decompiler/navigationMultipleLibs/expected.sources new file mode 100644 index 00000000000..3474389deca --- /dev/null +++ b/idea/testData/decompiler/navigationMultipleLibs/expected.sources @@ -0,0 +1,7 @@ + library.kt +class <1>LClass + +class <3>A +class B + +fun LClass.<2>f(a: A) { diff --git a/idea/testData/decompiler/navigationMultipleLibs/libSrc/library.kt b/idea/testData/decompiler/navigationMultipleLibs/libSrc/library.kt new file mode 100644 index 00000000000..048a313ad7a --- /dev/null +++ b/idea/testData/decompiler/navigationMultipleLibs/libSrc/library.kt @@ -0,0 +1,10 @@ +package lib + +class LClass + +class A +class B + +fun LClass.f(a: A) { + +} \ No newline at end of file diff --git a/idea/testData/decompiler/navigationMultipleLibs/src/module.kt b/idea/testData/decompiler/navigationMultipleLibs/src/module.kt new file mode 100644 index 00000000000..8979722cf3c --- /dev/null +++ b/idea/testData/decompiler/navigationMultipleLibs/src/module.kt @@ -0,0 +1,5 @@ +import lib.* + +fun LClass.g() { + f(A()) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/NavigationWithMultipleLibrariesTest.kt b/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/NavigationWithMultipleLibrariesTest.kt new file mode 100644 index 00000000000..0f1f6d78e69 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/NavigationWithMultipleLibrariesTest.kt @@ -0,0 +1,115 @@ +/* + * 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.idea.decompiler.navigation + +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.StdModuleTypes +import com.intellij.openapi.roots.DependencyScope +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable +import com.intellij.openapi.roots.libraries.Library +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import com.intellij.testFramework.ModuleTestCase +import org.jetbrains.kotlin.idea.caches.resolve.LibraryInfo +import org.jetbrains.kotlin.idea.caches.resolve.LibrarySourceInfo +import org.jetbrains.kotlin.idea.caches.resolve.getNullableModuleInfo +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir +import org.jetbrains.kotlin.test.MockLibraryUtil +import org.jetbrains.kotlin.test.testFramework.runWriteAction +import org.junit.Assert +import java.io.File + +val testDataPath = PluginTestCaseBase.getTestDataPathBase() + "/decompiler/navigationMultipleLibs/" + +class NavigationWithMultipleLibrariesTest : ModuleTestCase() { + + fun testNavigationToDecompiled() { + doTest(false, "expected.decompiled") + } + + fun testNavigationToLibrarySources() { + doTest(true, "expected.sources") + } + + fun doTest(withSources: Boolean, expectedFileName: String) { + val srcPath = testDataPath + "src" + val moduleA = module("moduleA", srcPath) + val moduleB = module("moduleB", srcPath) + + val librarySrc = testDataPath + "libSrc" + addDependencyOnProjectLibrary(moduleA, "libA", librarySrc, withSources) + addDependencyOnProjectLibrary(moduleB, "libB", librarySrc, withSources) + + // navigation code works by providing first matching declaration from indices + // that's we need to check references in both modules to guard against possibility of code breaking + // while tests pass by chance + checkReferencesInModule(moduleA, "libA", expectedFileName) + checkReferencesInModule(moduleB, "libB", expectedFileName) + } + + private fun module(name: String, srcPath: String) = createModuleFromTestData(srcPath, name, StdModuleTypes.JAVA, true)!! + + private fun checkReferencesInModule(moduleB: Module, libraryName: String, expectedFileName: String) { + NavigationChecker.checkAnnotatedCode(findSourceFile(moduleB), File(testDataPath + expectedFileName)) { + checkLibraryName(it, libraryName) + } + } + + private fun findSourceFile(moduleA: Module): PsiFile { + val ioFile = File(moduleA.getModuleDir()).listFiles()[0]; + val vFile = LocalFileSystem.getInstance().findFileByIoFile(ioFile)!! + return PsiManager.getInstance(project).findFile(vFile)!! + } + + private fun addDependencyOnProjectLibrary(mainModule: Module, libraryName: String, librarySrc: String, withSources: Boolean) { + val library = createProjectLib(libraryName, librarySrc, withSources) + ModuleRootModificationUtil.addDependency(mainModule, library, DependencyScope.COMPILE, false) + } + + private fun createProjectLib(libName: String, librarySrc: String, withSources: Boolean): Library { + val libraryJar = MockLibraryUtil.compileLibraryToJar(librarySrc, libName, withSources, false, false) + val jarUrl = StandardFileSystems.getJarRootForLocalFile(LocalFileSystem.getInstance().findFileByIoFile(libraryJar)!!)!! + return runWriteAction { + val library = ProjectLibraryTable.getInstance(project).createLibrary(libName) + val modifiableModel = library.modifiableModel + modifiableModel.addRoot(jarUrl, OrderRootType.CLASSES) + if (withSources) { + modifiableModel.addRoot(jarUrl.findChild("src")!!, OrderRootType.SOURCES) + } + modifiableModel.commit() + library + } + } +} + + +private fun checkLibraryName(referenceTarget: PsiElement, expectedName: String) { + val navigationFile = referenceTarget.navigationElement.containingFile ?: return + val libraryInfo = navigationFile.getNullableModuleInfo() + val libraryName = when (libraryInfo) { + is LibraryInfo -> libraryInfo.library.name + is LibrarySourceInfo -> libraryInfo.library.name + else -> error("Couldn't get library name") + } + Assert.assertEquals("Referenced code from unrelated library: ${referenceTarget.text}", expectedName, libraryName) +} \ No newline at end of file