IDEA-132625 (decompiler time limit)
[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.io.FileUtil;
25 import com.intellij.openapi.util.registry.Registry;
26 import com.intellij.openapi.util.registry.RegistryValue;
27 import com.intellij.openapi.vfs.*;
28 import com.intellij.pom.Navigatable;
29 import com.intellij.psi.PsiElement;
30 import com.intellij.psi.PsiFile;
31 import com.intellij.psi.impl.compiled.ClsFileImpl;
32 import com.intellij.testFramework.PlatformTestUtil;
33 import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
34 import com.intellij.util.ThrowableRunnable;
35 import com.intellij.util.containers.ContainerUtil;
36 import com.intellij.util.io.URLUtil;
37 import org.jetbrains.annotations.NotNull;
38
39 import java.util.Set;
40
41 public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase {
42   @Override
43   protected void setUp() throws Exception {
44     super.setUp();
45     myFixture.setTestDataPath(PluginPathManager.getPluginHomePath("java-decompiler") + "/plugin/testData");
46   }
47
48   public void testSimple() {
49     String path = PlatformTestUtil.getRtJarPath() + "!/java/lang/String.class";
50     VirtualFile file = getTestFile(path);
51     String decompiled = new IdeaDecompiler().getText(file).toString();
52     assertTrue(decompiled, decompiled.startsWith(IdeaDecompiler.BANNER + "package java.lang;\n"));
53     assertTrue(decompiled, decompiled.contains("public final class String"));
54     assertTrue(decompiled, decompiled.contains("@deprecated"));
55     assertTrue(decompiled, decompiled.contains("private static class CaseInsensitiveComparator"));
56     assertFalse(decompiled, decompiled.contains("{ /* compiled code */ }"));
57     assertFalse(decompiled, decompiled.contains("synthetic"));
58   }
59
60   public void testStubCompatibility() {
61     String path = PlatformTestUtil.getRtJarPath() + "!/java";
62     VirtualFile dir = getTestFile(path);
63     doTestStubCompatibility(dir);
64   }
65
66   private void doTestStubCompatibility(VirtualFile root) {
67     VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
68       @Override
69       public boolean visitFile(@NotNull VirtualFile file) {
70         if (file.isDirectory()) {
71           System.out.println(file.getPath());
72         }
73         else if (file.getFileType() == StdFileTypes.CLASS && !file.getName().contains("$") && !skip(file)) {
74           PsiFile clsFile = getPsiManager().findFile(file);
75           assertNotNull(file.getPath(), clsFile);
76           PsiElement mirror = ((ClsFileImpl)clsFile).getMirror();
77           String decompiled = mirror.getText();
78           assertTrue(file.getPath(), decompiled.contains(file.getNameWithoutExtension()));
79         }
80         return true;
81       }
82
83       private boolean skip(VirtualFile file) {
84         if (!SystemInfo.isJavaVersionAtLeast("1.8")) return false;
85         String path = file.getPath();
86         int p = path.indexOf("!/");
87         return p > 0 && knowProblems.contains(path.substring(p + 2));
88       }
89
90       // todo[r.sh] drop when IDEA-129734 get fixed
91       private final Set<String> knowProblems = ContainerUtil.newHashSet(
92         "java/lang/reflect/AnnotatedElement.class", "java/util/stream/Nodes.class", "java/util/stream/FindOps.class",
93         "java/util/stream/Collectors.class", "java/util/stream/DistinctOps.class", "java/util/stream/IntPipeline.class",
94         "java/util/stream/LongPipeline.class", "java/util/stream/DoublePipeline.class"
95       );
96     });
97   }
98
99   public void testNavigation() {
100     myFixture.openFileInEditor(getTestFile("Navigation.class"));
101     doTestNavigation(11, 14, 14, 10);  // to "m2()"
102     doTestNavigation(15, 21, 14, 17);  // to "int i"
103     doTestNavigation(16, 28, 15, 13);  // to "int r"
104   }
105
106   public void testHighlighting() {
107     myFixture.openFileInEditor(getTestFile("Navigation.class"));
108     IdentifierHighlighterPassFactory.doWithHighlightingEnabled(new Runnable() {
109       public void run() {
110         myFixture.getEditor().getCaretModel().moveToOffset(offset(11, 14));  // m2(): usage, declaration
111         assertEquals(2, myFixture.doHighlighting().size());
112         myFixture.getEditor().getCaretModel().moveToOffset(offset(15, 21));  // int i: usage, declaration
113         assertEquals(2, myFixture.doHighlighting().size());
114         myFixture.getEditor().getCaretModel().moveToOffset(offset(16, 28));  // int r: usage, declaration
115         assertEquals(2, myFixture.doHighlighting().size());
116         myFixture.getEditor().getCaretModel().moveToOffset(offset(19, 24));  // throws: declaration, m4() call
117         assertEquals(2, myFixture.doHighlighting().size());
118       }
119     });
120   }
121
122   private VirtualFile getTestFile(String name) {
123     String path = FileUtil.isAbsolute(name) ? name : myFixture.getTestDataPath() + "/" + name;
124     VirtualFileSystem fs = path.contains(URLUtil.JAR_SEPARATOR) ? StandardFileSystems.jar() : StandardFileSystems.local();
125     VirtualFile file = fs.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
162   public void testPerformance() {
163     final IdeaDecompiler decompiler = new IdeaDecompiler();
164     final VirtualFile file = getTestFile(PlatformTestUtil.getRtJarPath() + "!/javax/swing/JTable.class");
165     PlatformTestUtil.startPerformanceTest("decompiling JTable.class", 2500, new ThrowableRunnable() {
166       @Override
167       public void run() throws Throwable {
168         decompiler.getText(file);
169       }
170     }).cpuBound().assertTiming();
171   }
172 }