/* * Copyright 2000-2013 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 com.intellij.slicer; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.impl.ToolWindowHeadlessManagerImpl; import com.intellij.psi.*; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NonNls; import java.util.*; /** * @author cdr */ public class SliceTreeTest extends SliceTestCase { private SliceTreeStructure configureTree(@NonNls final String name) throws Exception { configureByFile("/codeInsight/slice/backward/"+ name +".java"); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); PsiElement element = new SliceHandler(true).getExpressionAtCaret(getEditor(), getFile()); assertNotNull(element); Collection errors = highlightErrors(); assertEmpty(errors); SliceAnalysisParams params = new SliceAnalysisParams(); params.scope = new AnalysisScope(getProject()); params.dataFlowToThis = true; SliceUsage usage = SliceUsage.createRootUsage(element, params); ToolWindowHeadlessManagerImpl.MockToolWindow toolWindow = new ToolWindowHeadlessManagerImpl.MockToolWindow(myProject); SlicePanel panel = new SlicePanel(getProject(), true, new SliceRootNode(getProject(), new DuplicateMap(), usage), false, toolWindow) { @Override protected void close() { } @Override public boolean isAutoScroll() { return false; } @Override public void setAutoScroll(boolean autoScroll) { } @Override public boolean isPreview() { return false; } @Override public void setPreview(boolean preview) { } }; Disposer.register(getProject(), panel); return (SliceTreeStructure)panel.getBuilder().getTreeStructure(); } private static void expandNodesTo(final SliceNode node, List to) { node.update(); node.calculateDupNode(); to.add(node); Collection nodes = node.getChildren(); for (AbstractTreeNode child : nodes) { expandNodesTo((SliceNode)child, to); } } public void testTypingDoesNotInterfereWithDuplicates() throws Exception { SliceTreeStructure treeStructure = configureTree("DupSlice"); SliceNode root = (SliceNode)treeStructure.getRootElement(); List nodes = new ArrayList(); expandNodesTo(root, nodes); TIntArrayList hasDups = new TIntArrayList(); for (SliceNode node : nodes) { if (node.getDuplicate() != null) { PsiElement element = node.getValue().getElement(); hasDups.add(element.getTextRange().getStartOffset()); assertTrue(element instanceof PsiParameter && ((PsiParameter)element).getName().equals("i") || element instanceof PsiLiteralExpression); } } type(" xx"); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); backspace(); backspace(); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); backspace(); backspace(); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); backspace(); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); nodes.clear(); expandNodesTo(root, nodes); for (SliceNode node : nodes) { if (node.getDuplicate() != null) { PsiElement element = node.getValue().getElement(); int offset = element.getTextRange().getStartOffset(); int i = hasDups.indexOf(offset); assertTrue(i != -1); hasDups.remove(i); assertTrue(element instanceof PsiParameter && ((PsiParameter)element).getName().equals("i") || element instanceof PsiLiteralExpression); } } assertTrue(hasDups.isEmpty()); } public void testLeafExpressionsAreEmptyInCaseOfInfinitelyExpandingTreeWithDuplicateNodes() throws Exception { SliceTreeStructure treeStructure = configureTree("Tuple"); SliceNode root = (SliceNode)treeStructure.getRootElement(); Collection leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, SliceLeafAnalyzer.createMap()); assertNotNull(leaves); assertEmpty(leaves); } public void testLeafExpressionsSimple() throws Exception { SliceTreeStructure treeStructure = configureTree("DupSlice"); SliceNode root = (SliceNode)treeStructure.getRootElement(); Collection leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, SliceLeafAnalyzer.createMap()); assertNotNull(leaves); PsiElement element = assertOneElement(leaves); assertTrue(element instanceof PsiLiteralExpression); assertEquals(1111111111, ((PsiLiteral)element).getValue()); } public void testLeafExpressionsMoreComplex() throws Exception { SliceTreeStructure treeStructure = configureTree("Duplicate"); SliceNode root = (SliceNode)treeStructure.getRootElement(); Map> map = SliceLeafAnalyzer.createMap(); Collection leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map); assertNotNull(leaves); List list = new ArrayList(leaves); String message = ContainerUtil.map(list, new Function() { @Override public String fun(PsiElement element) { return element.getClass() +": '"+element.getText()+"' ("+ SliceLeafAnalyzer.LEAF_ELEMENT_EQUALITY.computeHashCode(element)+") "; } }).toString(); assertEquals(map.entrySet()+"\n"+message, 2, leaves.size()); Collections.sort(list, new Comparator() { @Override public int compare(PsiElement o1, PsiElement o2) { return o1.getText().compareTo(o2.getText()); } }); assertTrue(list.get(0) instanceof PsiLiteralExpression); assertEquals(false, ((PsiLiteral)list.get(0)).getValue()); assertTrue(list.get(1) instanceof PsiLiteralExpression); assertEquals(true, ((PsiLiteral)list.get(1)).getValue()); } public void testGroupByValuesCorrectLeaves() throws Exception { SliceTreeStructure treeStructure = configureTree("DuplicateLeaves"); SliceRootNode root = (SliceRootNode)treeStructure.getRootElement(); Map> map = SliceLeafAnalyzer.createMap(); Collection leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map); assertNotNull(leaves); assertEquals(1, leaves.size()); PsiElement leaf = leaves.iterator().next(); assertTrue(leaf instanceof PsiLiteralExpression); assertEquals("\"oo\"", leaf.getText()); SliceRootNode newRoot = SliceLeafAnalyzer.createTreeGroupedByValues(leaves, root, map); Collection children = newRoot.getChildren(); assertEquals(1, children.size()); SliceNode child = (SliceNode)children.iterator().next(); assertTrue(child instanceof SliceLeafValueRootNode); children = child.getChildren(); assertEquals(1, children.size()); child = (SliceNode)children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiField); children = child.getChildren(); assertEquals(1, children.size()); child = (SliceNode)children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression); children = child.getChildren(); assertEquals(1, children.size()); child = (SliceNode)children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiParameter); children = child.getChildren(); assertEquals(1, children.size()); child = (SliceNode)children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression); children = child.getChildren(); assertEquals(1, children.size()); child = (SliceNode)children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiParameter); children = child.getChildren(); assertEquals(1, children.size()); child = (SliceNode)children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiLiteralExpression); assertEquals(child.getValue().getElement(), leaf); } public void testNullness() throws Exception { SliceTreeStructure treeStructure = configureTree("Nulls"); final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement(); Map map = SliceNullnessAnalyzer.createMap(); SliceNullnessAnalyzer.NullAnalysisResult leaves = SliceNullnessAnalyzer.calcNullableLeaves(root, treeStructure, map); SliceRootNode newRoot = SliceNullnessAnalyzer.createNewTree(leaves, root, map); checkStructure(newRoot, "Null Values\n" + " Value: o\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (15: 13) |set|(|o|)|;\n" + " Value: nu()\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (29: 13) |set|(|nu|(|)|)|;\n" + " Value: t\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (46: 15) |x|.|set|(|t|)|;\n" + "NotNull Values\n" + " Value: \"\"\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (19: 13) |set|(|CON|)|;\n" + " (5: 39) |private| |final| |static| |String| |CON| |=| |\"\"|;\n" + " Value: \"xxx\"\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (10: 13) |set|(|\"xxx\"|)|;\n" + " Value: new String()\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (17: 13) |set|(|new| |String|(|)|)|;\n" + " Value: nn()\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (18: 13) |set|(|nn|(|)|)|;\n" + " Value: nn\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (21: 13) |set|(|nn|)|;\n" + " Value: g\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (27: 13) |set|(|g|)|;\n" + " Value: \"null\"\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (48: 15) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" + " (48: 27) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" + " Value: t\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (48: 15) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" + " (48: 36) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" + " Value: d\n" + " (6: 12) |String| |l|;\n" + " (55: 13) |l| |=| |d|;\n" + "Other Values\n" + " Value: private String d;\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (30: 13) |set|(|hz|(|)|)|;\n" + " (42: 16) |return| |d|;\n" + " (7: 20) |private| |String| |d|;\n" + " Value: String g\n" + " (6: 12) |String| |l|;\n" + " (52: 13) |l| |=| |d|;\n" + " (51: 21) |void| |set|(|String| |d|)| |{\n" + " (11: 13) |set|(|g|)|;\n" + " (9: 21) |public| |X|(|String| |g|)| |{\n" + ""); } private static void checkStructure(final SliceNode root, @NonNls String dataExpected) { List actualNodes = new ArrayList((Collection)root.getChildren()); Collections.sort(actualNodes, SliceTreeBuilder.SLICE_NODE_COMPARATOR); Object[] actualStrings = ContainerUtil.map2Array(actualNodes, new Function() { @Override public Object fun(SliceNode node) { return node.toString(); } }); String[] childrenExpected = dataExpected.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : dataExpected.split("\n"); String curChildren = ""; String curNode = null; int iactual = 0; for (int iexp = 0; iexp <= childrenExpected.length; iexp++) { String e = iexp == childrenExpected.length ? null : childrenExpected[iexp]; boolean isTopLevel = e == null || e.charAt(0) != ' '; if (isTopLevel) { if (curNode != null) { assertTrue(iactual < actualStrings.length); Object actual = actualStrings[iactual]; assertEquals(curNode, actual); checkStructure(actualNodes.get(iactual), curChildren); iactual++; } curNode = e; curChildren = ""; } else { curChildren += StringUtil.trimStart(e, " ") + "\n"; } } assertEquals(dataExpected, actualNodes.size(), iactual); } public void testDoubleNullness() throws Exception { SliceTreeStructure treeStructure = configureTree("DoubleNulls"); final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement(); Map map = SliceNullnessAnalyzer.createMap(); SliceNullnessAnalyzer.NullAnalysisResult leaves = SliceNullnessAnalyzer.calcNullableLeaves(root, treeStructure, map); SliceRootNode newRoot = SliceNullnessAnalyzer.createNewTree(leaves, root, map); checkStructure(newRoot, "Null Values\n" + " Value: null\n" + " (2: 10) |String| |l|;\n" + " (4: 9) |l| |=| |null|;\n" + " (7: 9) |l| |=| |null|;\n" + "" ); } public void testGroupByLeavesWithLists() throws Exception { SliceTreeStructure treeStructure = configureTree(getTestName(false)); final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement(); Map> map = SliceLeafAnalyzer.createMap(); Collection leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map); assertEquals(2, leaves.size()); Set names = ContainerUtil.map2Set(leaves, new Function() { @Override public String fun(PsiElement element) { return element.getText(); } }); assertEquals(ContainerUtil.newHashSet("\"uuu\"", "\"xxx\""), names); } public void testCollectionTrack() throws Exception { Set names = groupByLeaves(); assertEquals(3, names.size()); assertEquals(ContainerUtil.newHashSet("\"uuu\"", "\"x\"", "\"y\""), names); } private Set groupByLeaves() throws Exception { SliceTreeStructure treeStructure = configureTree(getTestName(false)); final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement(); Map> map = SliceLeafAnalyzer.createMap(); Collection leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map); return ContainerUtil.map2Set(leaves, new Function() { @Override public String fun(PsiElement element) { return element.getText(); } }); } public void testArrayCopyTrack() throws Exception { Set names = groupByLeaves(); assertOrderedEquals(Collections.singletonList("\"x\""), assertOneElement(names)); } public void testMapValuesTrack() throws Exception { Set names = groupByLeaves(); assertOrderedEquals(Collections.singletonList("\"y\""), assertOneElement(names)); } public void testMapKeysTrack() throws Exception { Set names = groupByLeaves(); assertOrderedEquals(Collections.singletonList("\"x\""), assertOneElement(names)); } }