cdb4a0e8a9774de2dc23d6bdb8361bf8fe589069
[idea/community.git] / java / java-tests / testSrc / com / intellij / slicer / SliceTreeTest.java
1 /*
2  * Copyright 2000-2015 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 com.intellij.slicer;
17
18 import com.intellij.analysis.AnalysisScope;
19 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
20 import com.intellij.ide.util.treeView.AbstractTreeNode;
21 import com.intellij.openapi.util.Disposer;
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.openapi.wm.impl.ToolWindowHeadlessManagerImpl;
24 import com.intellij.psi.*;
25 import com.intellij.util.ArrayUtil;
26 import com.intellij.util.Function;
27 import com.intellij.util.containers.ContainerUtil;
28 import gnu.trove.TIntArrayList;
29 import org.jetbrains.annotations.NonNls;
30
31 import java.util.*;
32
33 /**
34  * @author cdr
35  */
36 public class SliceTreeTest extends SliceTestCase {
37   private SliceTreeStructure configureTree(@NonNls final String name) throws Exception {
38     configureByFile("/codeInsight/slice/backward/"+ name +".java");
39     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
40     PsiElement element = new SliceHandler(true).getExpressionAtCaret(getEditor(), getFile());
41     assertNotNull(element);
42     Collection<HighlightInfo> errors = highlightErrors();
43     assertEmpty(errors);
44
45     SliceAnalysisParams params = new SliceAnalysisParams();
46     params.scope = new AnalysisScope(getProject());
47     params.dataFlowToThis = true;
48
49     SliceUsage usage = LanguageSlicing.getProvider(element).createRootUsage(element, params);
50
51
52     ToolWindowHeadlessManagerImpl.MockToolWindow toolWindow = new ToolWindowHeadlessManagerImpl.MockToolWindow(myProject);
53     SlicePanel panel = new SlicePanel(getProject(), true, new SliceRootNode(getProject(), new DuplicateMap(), usage), false, toolWindow) {
54       @Override
55       protected void close() {
56       }
57
58       @Override
59       public boolean isAutoScroll() {
60         return false;
61       }
62
63       @Override
64       public void setAutoScroll(boolean autoScroll) {
65       }
66
67       @Override
68       public boolean isPreview() {
69         return false;
70       }
71
72       @Override
73       public void setPreview(boolean preview) {
74       }
75     };
76     Disposer.register(getProject(), panel);
77     return (SliceTreeStructure)panel.getBuilder().getTreeStructure();
78   }
79
80   private static void expandNodesTo(final SliceNode node, List<SliceNode> to) {
81     node.update();
82     node.calculateDupNode();
83     to.add(node);
84     Collection<? extends AbstractTreeNode> nodes = node.getChildren();
85     for (AbstractTreeNode child : nodes) {
86       expandNodesTo((SliceNode)child, to);
87     }
88   }
89
90   public void testTypingDoesNotInterfereWithDuplicates() throws Exception {
91     SliceTreeStructure treeStructure = configureTree("DupSlice");
92     SliceNode root = (SliceNode)treeStructure.getRootElement();
93     List<SliceNode> nodes = new ArrayList<SliceNode>();
94     expandNodesTo(root, nodes);
95
96     TIntArrayList hasDups = new TIntArrayList();
97     for (SliceNode node : nodes) {
98       if (node.getDuplicate() != null) {
99         PsiElement element = node.getValue().getElement();
100         hasDups.add(element.getTextRange().getStartOffset());
101         assertTrue(element instanceof PsiParameter && ((PsiParameter)element).getName().equals("i") || element instanceof PsiLiteralExpression);
102       }
103     }
104
105     type("   xx");
106     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
107     backspace();
108     backspace();
109     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
110     backspace();
111     backspace();
112     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
113     backspace();
114     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
115
116     nodes.clear();
117     expandNodesTo(root, nodes);
118     for (SliceNode node : nodes) {
119       if (node.getDuplicate() != null) {
120         PsiElement element = node.getValue().getElement();
121         int offset = element.getTextRange().getStartOffset();
122         int i = hasDups.indexOf(offset);
123         assertTrue(i != -1);
124         hasDups.remove(i);
125         assertTrue(element instanceof PsiParameter && ((PsiParameter)element).getName().equals("i") || element instanceof PsiLiteralExpression);
126       }
127     }
128     assertTrue(hasDups.isEmpty());
129   }
130
131   public void testLeafExpressionsAreEmptyInCaseOfInfinitelyExpandingTreeWithDuplicateNodes() throws Exception {
132     SliceTreeStructure treeStructure = configureTree("Tuple");
133     SliceNode root = (SliceNode)treeStructure.getRootElement();
134     Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, SliceLeafAnalyzer.createMap());
135     assertNotNull(leaves);
136     assertEmpty(leaves);
137   }
138   public void testLeafExpressionsSimple() throws Exception {
139     SliceTreeStructure treeStructure = configureTree("DupSlice");
140     SliceNode root = (SliceNode)treeStructure.getRootElement();
141     Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, SliceLeafAnalyzer.createMap());
142     assertNotNull(leaves);
143     PsiElement element = assertOneElement(leaves);
144     assertTrue(element instanceof PsiLiteralExpression);
145     assertEquals(1111111111, ((PsiLiteral)element).getValue());
146   }
147   public void testLeafExpressionsMoreComplex() throws Exception {
148     SliceTreeStructure treeStructure = configureTree("Duplicate");
149     SliceNode root = (SliceNode)treeStructure.getRootElement();
150     Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
151     Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
152     assertNotNull(leaves);
153     List<PsiElement> list = new ArrayList<PsiElement>(leaves);
154     String message = ContainerUtil.map(list, new Function<PsiElement, String>() {
155       @Override
156       public String fun(PsiElement element) {
157         return element.getClass() +": '"+element.getText()+"' ("+ SliceLeafAnalyzer.LEAF_ELEMENT_EQUALITY.computeHashCode(element)+") ";
158       }
159     }).toString();
160     assertEquals(map.entrySet()+"\n"+message, 2, leaves.size());
161     Collections.sort(list, new Comparator<PsiElement>() {
162       @Override
163       public int compare(PsiElement o1, PsiElement o2) {
164         return o1.getText().compareTo(o2.getText());
165       }
166     });
167     assertTrue(list.get(0) instanceof PsiLiteralExpression);
168     assertEquals(false, ((PsiLiteral)list.get(0)).getValue());
169     assertTrue(list.get(1) instanceof PsiLiteralExpression);
170     assertEquals(true, ((PsiLiteral)list.get(1)).getValue());
171   }
172
173   public void testGroupByValuesCorrectLeaves() throws Exception {
174     SliceTreeStructure treeStructure = configureTree("DuplicateLeaves");
175     SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
176     Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
177     Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
178     assertNotNull(leaves);
179     assertEquals(1, leaves.size());
180     PsiElement leaf = leaves.iterator().next();
181     assertTrue(leaf instanceof PsiLiteralExpression);
182     assertEquals("\"oo\"", leaf.getText());
183
184     SliceRootNode newRoot = SliceLeafAnalyzer.createTreeGroupedByValues(leaves, root, map);
185     Collection<? extends AbstractTreeNode> children = newRoot.getChildren();
186     assertEquals(1, children.size());
187     SliceNode child = (SliceNode)children.iterator().next();
188     assertTrue(child instanceof SliceLeafValueRootNode);
189
190     children = child.getChildren();
191     assertEquals(1, children.size());
192     child = (SliceNode)children.iterator().next();
193     assertTrue(child.getValue().getElement() instanceof PsiField);
194
195     children = child.getChildren();
196     assertEquals(1, children.size());
197     child = (SliceNode)children.iterator().next();
198     assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression);
199
200     children = child.getChildren();
201     assertEquals(1, children.size());
202     child = (SliceNode)children.iterator().next();
203     assertTrue(child.getValue().getElement() instanceof PsiParameter);
204
205     children = child.getChildren();
206     assertEquals(1, children.size());
207     child = (SliceNode)children.iterator().next();
208     assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression);
209
210     children = child.getChildren();
211     assertEquals(1, children.size());
212     child = (SliceNode)children.iterator().next();
213     assertTrue(child.getValue().getElement() instanceof PsiParameter);
214
215     children = child.getChildren();
216     assertEquals(1, children.size());
217     child = (SliceNode)children.iterator().next();
218     assertTrue(child.getValue().getElement() instanceof PsiLiteralExpression);
219     assertEquals(child.getValue().getElement(), leaf);
220   }
221
222   public void testNullness() throws Exception {
223     SliceTreeStructure treeStructure = configureTree("Nulls");
224     final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
225     Map<SliceNode, SliceNullnessAnalyzer.NullAnalysisResult> map = SliceNullnessAnalyzer.createMap();
226     SliceNullnessAnalyzer.NullAnalysisResult leaves = SliceNullnessAnalyzer.calcNullableLeaves(root, treeStructure, map);
227
228     SliceRootNode newRoot = SliceNullnessAnalyzer.createNewTree(leaves, root, map);
229
230     checkStructure(newRoot, "Null Values\n" +
231                             "  Value: o\n" +
232                             "    6|String| |l|;\n" +
233                             "      52|l| |=| |d|;\n" +
234                             "        51|void| |set|(|String| |d|)| |{\n" +
235                             "          15|set|(|o|)|;\n" +
236                             "  Value: nu()\n" +
237                             "    6|String| |l|;\n" +
238                             "      52|l| |=| |d|;\n" +
239                             "        51|void| |set|(|String| |d|)| |{\n" +
240                             "          29|set|(|nu|(|)|)|;\n" +
241                             "  Value: t\n" +
242                             "    6|String| |l|;\n" +
243                             "      52|l| |=| |d|;\n" +
244                             "        51|void| |set|(|String| |d|)| |{\n" +
245                             "          46|x|.|set|(|t|)|;\n" +
246                             "NotNull Values\n" +
247                             "  Value: \"\"\n" +
248                             "    6|String| |l|;\n" +
249                             "      52|l| |=| |d|;\n" +
250                             "        51|void| |set|(|String| |d|)| |{\n" +
251                             "          19|set|(|CON|)|;\n" +
252                             "            5|private| |final| |static| |String| |CON| |=| |\"\"|;\n" +
253                             "  Value: \"xxx\"\n" +
254                             "    6|String| |l|;\n" +
255                             "      52|l| |=| |d|;\n" +
256                             "        51|void| |set|(|String| |d|)| |{\n" +
257                             "          10|set|(|\"xxx\"|)|;\n" +
258                             "  Value: new String()\n" +
259                             "    6|String| |l|;\n" +
260                             "      52|l| |=| |d|;\n" +
261                             "        51|void| |set|(|String| |d|)| |{\n" +
262                             "          17|set|(|new| |String|(|)|)|;\n" +
263                             "  Value: nn()\n" +
264                             "    6|String| |l|;\n" +
265                             "      52|l| |=| |d|;\n" +
266                             "        51|void| |set|(|String| |d|)| |{\n" +
267                             "          18|set|(|nn|(|)|)|;\n" +
268                             "  Value: nn\n" +
269                             "    6|String| |l|;\n" +
270                             "      52|l| |=| |d|;\n" +
271                             "        51|void| |set|(|String| |d|)| |{\n" +
272                             "          21|set|(|nn|)|;\n" +
273                             "  Value: g\n" +
274                             "    6|String| |l|;\n" +
275                             "      52|l| |=| |d|;\n" +
276                             "        51|void| |set|(|String| |d|)| |{\n" +
277                             "          27|set|(|g|)|;\n" +
278                             "  Value: \"null\"\n" +
279                             "    6|String| |l|;\n" +
280                             "      52|l| |=| |d|;\n" +
281                             "        51|void| |set|(|String| |d|)| |{\n" +
282                             "          48|x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
283                             "            48|x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
284                             "  Value: t\n" +
285                             "    6|String| |l|;\n" +
286                             "      52|l| |=| |d|;\n" +
287                             "        51|void| |set|(|String| |d|)| |{\n" +
288                             "          48|x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
289                             "            48|x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
290                             "  Value: d\n" +
291                             "    6|String| |l|;\n" +
292                             "      55|l| |=| |d|;\n" +
293                             "Other Values\n" +
294                             "  Value: private String d;\n" +
295                             "    6|String| |l|;\n" +
296                             "      52|l| |=| |d|;\n" +
297                             "        51|void| |set|(|String| |d|)| |{\n" +
298                             "          30|set|(|hz|(|)|)|;\n" +
299                             "            42|return| |d|;\n" +
300                             "              7|private| |String| |d|;\n" +
301                             "  Value: String g\n" +
302                             "    6|String| |l|;\n" +
303                             "      52|l| |=| |d|;\n" +
304                             "        51|void| |set|(|String| |d|)| |{\n" +
305                             "          11|set|(|g|)|;\n" +
306                             "            9|public| |X|(|String| |g|)| |{\n" +
307                             "");
308   }
309
310   private static void checkStructure(final SliceNode root, @NonNls String dataExpected) {
311     List<SliceNode> actualNodes = new ArrayList<SliceNode>((Collection)root.getChildren());
312     Collections.sort(actualNodes, SliceTreeBuilder.SLICE_NODE_COMPARATOR);
313
314     Object[] actualStrings = ContainerUtil.map2Array(actualNodes, new Function<SliceNode, Object>() {
315       @Override
316       public Object fun(SliceNode node) {
317         return node.toString();
318       }
319     });
320
321     String[] childrenExpected = dataExpected.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : dataExpected.split("\n");
322     String curChildren = "";
323     String curNode = null;
324     int iactual = 0;
325     for (int iexp = 0; iexp <= childrenExpected.length; iexp++) {
326       String e = iexp == childrenExpected.length ? null : childrenExpected[iexp];
327       boolean isTopLevel = e == null || e.charAt(0) != ' ';
328       if (isTopLevel) {
329         if (curNode != null) {
330           assertTrue(iactual < actualStrings.length);
331           Object actual = actualStrings[iactual];
332           assertEquals(curNode, actual);
333           checkStructure(actualNodes.get(iactual), curChildren);
334           iactual++;
335         }
336
337         curNode = e;
338         curChildren = "";
339       }
340       else {
341         curChildren += StringUtil.trimStart(e, "  ") + "\n";
342       }
343     }
344     assertEquals(dataExpected, actualNodes.size(), iactual);
345   }
346
347   public void testDoubleNullness() throws Exception {
348     SliceTreeStructure treeStructure = configureTree("DoubleNulls");
349     final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
350     Map<SliceNode, SliceNullnessAnalyzer.NullAnalysisResult> map = SliceNullnessAnalyzer.createMap();
351     SliceNullnessAnalyzer.NullAnalysisResult leaves = SliceNullnessAnalyzer.calcNullableLeaves(root, treeStructure, map);
352
353     SliceRootNode newRoot = SliceNullnessAnalyzer.createNewTree(leaves, root, map);
354     checkStructure(newRoot,
355         "Null Values\n" +
356         "  Value: null\n" +
357         "    2|String| |l|;\n" +
358         "      4|l| |=| |null|;\n" +
359         "      7|l| |=| |null|;\n" +
360         ""
361                    );
362   }
363
364   public void testGroupByLeavesWithLists() throws Exception {
365     SliceTreeStructure treeStructure = configureTree(getTestName(false));
366     final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
367     Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
368     Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
369     assertEquals(2, leaves.size());
370     Set<String> names = ContainerUtil.map2Set(leaves, new Function<PsiElement, String>() {
371       @Override
372       public String fun(PsiElement element) {
373         return element.getText();
374       }
375     });
376     assertEquals(ContainerUtil.newHashSet("\"uuu\"", "\"xxx\""), names);
377   }
378
379   public void testCollectionTrack() throws Exception {
380     Set<String> names = groupByLeaves();
381     assertEquals(3, names.size());
382     assertEquals(ContainerUtil.newHashSet("\"uuu\"", "\"x\"", "\"y\""), names);
383   }
384
385   private Set<String> groupByLeaves() throws Exception {
386     SliceTreeStructure treeStructure = configureTree(getTestName(false));
387     final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
388     Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
389     Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
390     return ContainerUtil.map2Set(leaves, new Function<PsiElement, String>() {
391       @Override
392       public String fun(PsiElement element) {
393         return element.getText();
394       }
395     });
396   }
397
398   public void testArrayCopyTrack() throws Exception {
399     Set<String> names = groupByLeaves();
400     assertOrderedEquals(Collections.singletonList("\"x\""), assertOneElement(names));
401   }
402   public void testMapValuesTrack() throws Exception {
403     Set<String> names = groupByLeaves();
404     assertOrderedEquals(Collections.singletonList("\"y\""), assertOneElement(names));
405   }
406   public void testMapKeysTrack() throws Exception {
407     Set<String> names = groupByLeaves();
408     assertOrderedEquals(Collections.singletonList("\"x\""), assertOneElement(names));
409   }
410 }