1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.File; 25 import java.io.IOException; 26 import java.io.StringWriter; 27 import java.nio.charset.StandardCharsets; 28 import java.nio.file.Files; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Set; 34 import java.util.stream.Collectors; 35 36 import javax.lang.model.element.Modifier; 37 import javax.tools.JavaCompiler; 38 import javax.tools.JavaFileObject; 39 import javax.tools.StandardJavaFileManager; 40 import javax.tools.ToolProvider; 41 42 import com.sun.source.tree.BlockTree; 43 import com.sun.source.tree.BreakTree; 44 import com.sun.source.tree.CaseTree; 45 import com.sun.source.tree.ClassTree; 46 import com.sun.source.tree.CompilationUnitTree; 47 import com.sun.source.tree.ContinueTree; 48 import com.sun.source.tree.DoWhileLoopTree; 49 import com.sun.source.tree.ExpressionStatementTree; 50 import com.sun.source.tree.ForLoopTree; 51 import com.sun.source.tree.IfTree; 52 import com.sun.source.tree.ImportTree; 53 import com.sun.source.tree.LabeledStatementTree; 54 import com.sun.source.tree.LineMap; 55 import com.sun.source.tree.MethodTree; 56 import com.sun.source.tree.ReturnTree; 57 import com.sun.source.tree.StatementTree; 58 import com.sun.source.tree.SwitchTree; 59 import com.sun.source.tree.Tree; 60 import com.sun.source.tree.WhileLoopTree; 61 import com.sun.source.util.SourcePositions; 62 import com.sun.source.util.Trees; 63 import com.sun.tools.javac.api.JavacTaskImpl; 64 65 import jdk.jshell.SourceCodeAnalysis; 66 67 import org.testng.annotations.DataProvider; 68 import org.testng.annotations.Test; 69 70 import static java.lang.Integer.max; 71 import static java.lang.Integer.min; 72 import static jdk.jshell.SourceCodeAnalysis.Completeness.*; 73 74 public class CompletenessStressTest extends KullaTesting { 75 public final static String JDK_ROOT_SRC_PROP = "jdk.root.src"; 76 public final static String JDK_ROOT_SRC; 77 78 static { 79 JDK_ROOT_SRC = System.getProperty(JDK_ROOT_SRC_PROP); 80 } 81 getSourceFile(String fileName)82 public File getSourceFile(String fileName) { 83 for (File dir : getDirectoriesToTest()) { 84 File file = new File(dir, fileName); 85 if (file.exists()) { 86 return file; 87 } 88 } 89 throw new AssertionError("File not found: " + fileName); 90 } 91 getDirectoriesToTest()92 public File[] getDirectoriesToTest() { 93 return new File[]{ 94 new File(JDK_ROOT_SRC, "nashorn/src"), 95 new File(JDK_ROOT_SRC, "langtools/src"), 96 new File(JDK_ROOT_SRC, "jaxp/src"), 97 new File(JDK_ROOT_SRC, "jaxws/src"), 98 new File(JDK_ROOT_SRC, "jdk/src"), 99 new File(JDK_ROOT_SRC, "corba/src") 100 }; 101 } 102 103 @DataProvider(name = "crawler") dataProvider()104 public Object[][] dataProvider() throws IOException { 105 File[] srcDirs = getDirectoriesToTest(); 106 List<String[]> list = new ArrayList<>(); 107 for (File srcDir : srcDirs) { 108 String srcDirName = srcDir.getAbsolutePath(); 109 // this is just to obtain pretty test names for testng tests 110 List<String[]> a = Files.walk(Paths.get(srcDirName)) 111 .map(Path::toFile) 112 .map(File::getAbsolutePath) 113 .filter(n -> n.endsWith(".java")) 114 .map(n -> n.replace(srcDirName, "")) 115 .map(n -> new String[]{n}) 116 .collect(Collectors.toList()); 117 if (a.isEmpty()) { 118 throw new AssertionError("Java sources have not been found in directory: " + srcDirName); 119 } 120 list.addAll(a); 121 } 122 return list.toArray(new String[list.size()][]); 123 } 124 125 @Test(dataProvider = "crawler") testFile(String fileName)126 public void testFile(String fileName) throws IOException { 127 File file = getSourceFile(fileName); 128 final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 129 final StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); 130 boolean success = true; 131 StringWriter writer = new StringWriter(); 132 writer.write("Testing : " + file.toString() + "\n"); 133 String text = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); 134 Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file); 135 JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fileManager, null, null, null, compilationUnits); 136 Iterable<? extends CompilationUnitTree> asts = task.parse(); 137 Trees trees = Trees.instance(task); 138 SourcePositions sp = trees.getSourcePositions(); 139 140 for (CompilationUnitTree cut : asts) { 141 for (ImportTree imp : cut.getImports()) { 142 success &= testStatement(writer, sp, text, cut, imp); 143 } 144 for (Tree decl : cut.getTypeDecls()) { 145 success &= testStatement(writer, sp, text, cut, decl); 146 if (decl instanceof ClassTree) { 147 ClassTree ct = (ClassTree) decl; 148 for (Tree mem : ct.getMembers()) { 149 if (mem instanceof MethodTree) { 150 MethodTree mt = (MethodTree) mem; 151 BlockTree bt = mt.getBody(); 152 // No abstract methods or constructors 153 if (bt != null && mt.getReturnType() != null) { 154 // The modifiers synchronized, abstract, and default are not allowed on 155 // top-level declarations and are errors. 156 Set<Modifier> modifier = mt.getModifiers().getFlags(); 157 if (!modifier.contains(Modifier.ABSTRACT) 158 && !modifier.contains(Modifier.SYNCHRONIZED) 159 && !modifier.contains(Modifier.DEFAULT)) { 160 success &= testStatement(writer, sp, text, cut, mt); 161 } 162 testBlock(writer, sp, text, cut, bt); 163 } 164 } 165 } 166 } 167 } 168 } 169 fileManager.close(); 170 if (!success) { 171 throw new AssertionError(writer.toString()); 172 } 173 } 174 isLegal(StatementTree st)175 private boolean isLegal(StatementTree st) { 176 return !(st instanceof ReturnTree) && 177 !(st instanceof ContinueTree) && !(st instanceof BreakTree); 178 } 179 testBranch(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, StatementTree statementTree)180 private boolean testBranch(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, StatementTree statementTree) { 181 if (statementTree instanceof BlockTree) { 182 return testBlock(writer, sp, text, cut, (BlockTree) statementTree); 183 } else if (isLegal(statementTree)) { 184 return testStatement(writer, sp, text, cut, statementTree); 185 } 186 return true; 187 } 188 testBlock(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, BlockTree blockTree)189 private boolean testBlock(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, BlockTree blockTree) { 190 boolean success = true; 191 for (StatementTree st : blockTree.getStatements()) { 192 if (isLegal(st)) { 193 success &= testStatement(writer, sp, text, cut, st); 194 } 195 if (st instanceof IfTree) { 196 IfTree ifTree = (IfTree) st; 197 success &= testBranch(writer, sp, text, cut, ifTree.getThenStatement()); 198 success &= testBranch(writer, sp, text, cut, ifTree.getElseStatement()); 199 } else if (st instanceof WhileLoopTree) { 200 WhileLoopTree whileLoopTree = (WhileLoopTree) st; 201 success &= testBranch(writer, sp, text, cut, whileLoopTree.getStatement()); 202 } else if (st instanceof DoWhileLoopTree) { 203 DoWhileLoopTree doWhileLoopTree = (DoWhileLoopTree) st; 204 success &= testBranch(writer, sp, text, cut, doWhileLoopTree.getStatement()); 205 } else if (st instanceof ForLoopTree) { 206 ForLoopTree forLoopTree = (ForLoopTree) st; 207 success &= testBranch(writer, sp, text, cut, forLoopTree.getStatement()); 208 } else if (st instanceof LabeledStatementTree) { 209 LabeledStatementTree labelTree = (LabeledStatementTree) st; 210 success &= testBranch(writer, sp, text, cut, labelTree.getStatement()); 211 } else if (st instanceof SwitchTree) { 212 SwitchTree switchTree = (SwitchTree) st; 213 for (CaseTree caseTree : switchTree.getCases()) { 214 for (StatementTree statementTree : caseTree.getStatements()) { 215 success &= testBranch(writer, sp, text, cut, statementTree); 216 } 217 } 218 } 219 } 220 return success; 221 } 222 testStatement(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, Tree statement)223 private boolean testStatement(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, Tree statement) { 224 if (statement == null) { 225 return true; 226 } 227 int start = (int) sp.getStartPosition(cut, statement); 228 int end = (int) sp.getEndPosition(cut, statement); 229 char ch = text.charAt(end - 1); 230 SourceCodeAnalysis.Completeness expected = COMPLETE; 231 LineMap lineMap = cut.getLineMap(); 232 int row = (int) lineMap.getLineNumber(start); 233 int column = (int) lineMap.getColumnNumber(start); 234 switch (ch) { 235 case ',': 236 case ';': 237 expected = (statement instanceof ExpressionStatementTree) 238 ? COMPLETE 239 : COMPLETE_WITH_SEMI; 240 --end; 241 break; 242 case '}': 243 break; 244 default: 245 writer.write(String.format("Unexpected end: row %d, column %d: '%c' -- %s\n", 246 row, column, ch, text.substring(start, end))); 247 return true; 248 } 249 String unit = text.substring(start, end); 250 SourceCodeAnalysis.CompletionInfo ci = getAnalysis().analyzeCompletion(unit); 251 if (ci.completeness() != expected) { 252 if (expected == COMPLETE_WITH_SEMI && (ci.completeness() == CONSIDERED_INCOMPLETE || ci.completeness() == EMPTY)) { 253 writer.write(String.format("Empty statement: row %d, column %d: -- %s\n", 254 start, end, unit)); 255 } else { 256 writer.write(String.format("Expected %s got %s: '%s' row %d, column %d: -- %s\n", 257 expected, ci.completeness(), unit, row, column, unit)); 258 return false; 259 } 260 } 261 return true; 262 } 263 } 264