Merge remote-tracking branch 'origin/master'
[idea/community.git] / plugins / java-decompiler / plugin / test / org / jetbrains / java / decompiler / IdeaDecompilerTest.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.java.decompiler;
17
18 import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory;
19 import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
20 import com.intellij.debugger.PositionManager;
21 import com.intellij.openapi.application.PluginPathManager;
22 import com.intellij.openapi.fileTypes.StdFileTypes;
23 import com.intellij.openapi.util.SystemInfo;
24 import com.intellij.openapi.util.registry.Registry;
25 import com.intellij.openapi.util.registry.RegistryValue;
26 import com.intellij.openapi.vfs.StandardFileSystems;
27 import com.intellij.openapi.vfs.VfsUtilCore;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.vfs.VirtualFileVisitor;
30 import com.intellij.pom.Navigatable;
31 import com.intellij.psi.PsiElement;
32 import com.intellij.psi.PsiFile;
33 import com.intellij.psi.impl.compiled.ClsFileImpl;
34 import com.intellij.testFramework.PlatformTestUtil;
35 import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
36 import com.intellij.util.containers.ContainerUtil;
37 import org.jetbrains.annotations.NotNull;
38
39 import java.util.Set;
40
41 public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase {
42   public void testSimple() {
43     String path = PlatformTestUtil.getRtJarPath() + "!/java/lang/String.class";
44     VirtualFile file = StandardFileSystems.jar().findFileByPath(path);
45     assertNotNull(path, file);
46
47     String decompiled = new IdeaDecompiler().getText(file).toString();
48     assertTrue(decompiled, decompiled.startsWith(IdeaDecompiler.BANNER + "package java.lang;\n"));
49     assertTrue(decompiled, decompiled.contains("public final class String"));
50     assertTrue(decompiled, decompiled.contains("@deprecated"));
51     assertTrue(decompiled, decompiled.contains("private static class CaseInsensitiveComparator"));
52     assertFalse(decompiled, decompiled.contains("{ /* compiled code */ }"));
53     assertFalse(decompiled, decompiled.contains("synthetic"));
54   }
55
56   public void testStubCompatibility() {
57     String path = PlatformTestUtil.getRtJarPath() + "!/java";
58     VirtualFile dir = StandardFileSystems.jar().findFileByPath(path);
59     assertNotNull(path, dir);
60     doTestStubCompatibility(dir);
61   }
62
63   private void doTestStubCompatibility(VirtualFile root) {
64     VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
65       @Override
66       public boolean visitFile(@NotNull VirtualFile file) {
67         if (file.isDirectory()) {
68           System.out.println(file.getPath());
69         }
70         else if (file.getFileType() == StdFileTypes.CLASS && !file.getName().contains("$") && !skip(file)) {
71           PsiFile clsFile = getPsiManager().findFile(file);
72           assertNotNull(file.getPath(), clsFile);
73           PsiElement mirror = ((ClsFileImpl)clsFile).getMirror();
74           String decompiled = mirror.getText();
75           assertTrue(file.getPath(), decompiled.contains(file.getNameWithoutExtension()));
76         }
77         return true;
78       }
79
80       private boolean skip(VirtualFile file) {
81         if (!SystemInfo.isJavaVersionAtLeast("1.8")) return false;
82         String path = file.getPath();
83         int p = path.indexOf("!/");
84         return p > 0 && knowProblems.contains(path.substring(p + 2));
85       }
86
87       // todo[r.sh] drop when IDEA-129734 get fixed
88       private final Set<String> knowProblems = ContainerUtil.newHashSet(
89         "java/lang/reflect/AnnotatedElement.class", "java/util/stream/Nodes.class", "java/util/stream/FindOps.class",
90         "java/util/stream/Collectors.class", "java/util/stream/DistinctOps.class", "java/util/stream/IntPipeline.class",
91         "java/util/stream/LongPipeline.class", "java/util/stream/DoublePipeline.class"
92       );
93     });
94   }
95
96   public void testNavigation() {
97     VirtualFile file = getTestFile("Navigation.class");
98     myFixture.openFileInEditor(file);
99
100     doTestNavigation(11, 14, 14, 10);  // to "m2()"
101     doTestNavigation(15, 21, 14, 17);  // to "int i"
102     doTestNavigation(16, 28, 15, 13);  // to "int r"
103   }
104
105   public void testHighlighting() {
106     VirtualFile file = getTestFile("Navigation.class");
107     myFixture.openFileInEditor(file);
108
109     IdentifierHighlighterPassFactory.doWithHighlightingEnabled(new Runnable() {
110       public void run() {
111         myFixture.getEditor().getCaretModel().moveToOffset(offset(11, 14));  // m2(): usage, declaration
112         assertEquals(2, myFixture.doHighlighting().size());
113         myFixture.getEditor().getCaretModel().moveToOffset(offset(15, 21));  // int i: usage, declaration
114         assertEquals(2, myFixture.doHighlighting().size());
115         myFixture.getEditor().getCaretModel().moveToOffset(offset(16, 28));  // int r: usage, declaration
116         assertEquals(2, myFixture.doHighlighting().size());
117         myFixture.getEditor().getCaretModel().moveToOffset(offset(19, 24));  // throws: declaration, m4() call
118         assertEquals(2, myFixture.doHighlighting().size());
119       }
120     });
121   }
122
123   private static VirtualFile getTestFile(String name) {
124     String path = PluginPathManager.getPluginHomePath("java-decompiler") + "/plugin/testData/" + name;
125     VirtualFile file = StandardFileSystems.local().refreshAndFindFileByPath(path);
126     assertNotNull(path, file);
127     return file;
128   }
129
130   private void doTestNavigation(int line, int column, int expectedLine, int expectedColumn) {
131     PsiElement target = GotoDeclarationAction.findTargetElement(getProject(), myFixture.getEditor(), offset(line, column));
132     assertTrue(String.valueOf(target), target instanceof Navigatable);
133     ((Navigatable)target).navigate(true);
134     int expected = offset(expectedLine, expectedColumn);
135     assertEquals(expected, myFixture.getCaretOffset());
136   }
137
138   private int offset(int line, int column) {
139     return myFixture.getEditor().getDocument().getLineStartOffset(line - 1) + column - 1;
140   }
141
142   public void testLineNumberMapping() {
143     RegistryValue value = Registry.get("decompiler.use.line.mapping");
144     boolean old = value.asBoolean();
145     try {
146       value.setValue(true);
147
148       VirtualFile file = getTestFile("LineNumbers.class");
149       assertNull(file.getUserData(PositionManager.LINE_NUMBERS_MAPPING_KEY));
150
151       new IdeaDecompiler().getText(file);
152
153       int[] mapping = file.getUserData(PositionManager.LINE_NUMBERS_MAPPING_KEY);
154       assertNotNull(mapping);
155       assertEquals(20, mapping.length);
156     }
157     finally {
158       value.setValue(old);
159     }
160   }
161 }