/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.lang.model.element.Modifier; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import com.sun.source.tree.BlockTree; import com.sun.source.tree.BreakTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ContinueTree; import com.sun.source.tree.DoWhileLoopTree; import com.sun.source.tree.ExpressionStatementTree; import com.sun.source.tree.ForLoopTree; import com.sun.source.tree.IfTree; import com.sun.source.tree.ImportTree; import com.sun.source.tree.LabeledStatementTree; import com.sun.source.tree.LineMap; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.SwitchTree; import com.sun.source.tree.Tree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; import com.sun.tools.javac.api.JavacTaskImpl; import jdk.jshell.SourceCodeAnalysis; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static java.lang.Integer.max; import static java.lang.Integer.min; import static jdk.jshell.SourceCodeAnalysis.Completeness.*; public class CompletenessStressTest extends KullaTesting { public final static String JDK_ROOT_SRC_PROP = "jdk.root.src"; public final static String JDK_ROOT_SRC; static { JDK_ROOT_SRC = System.getProperty(JDK_ROOT_SRC_PROP); } public File getSourceFile(String fileName) { for (File dir : getDirectoriesToTest()) { File file = new File(dir, fileName); if (file.exists()) { return file; } } throw new AssertionError("File not found: " + fileName); } public File[] getDirectoriesToTest() { return new File[]{ new File(JDK_ROOT_SRC, "nashorn/src"), new File(JDK_ROOT_SRC, "langtools/src"), new File(JDK_ROOT_SRC, "jaxp/src"), new File(JDK_ROOT_SRC, "jaxws/src"), new File(JDK_ROOT_SRC, "jdk/src"), new File(JDK_ROOT_SRC, "corba/src") }; } @DataProvider(name = "crawler") public Object[][] dataProvider() throws IOException { File[] srcDirs = getDirectoriesToTest(); List<String[]> list = new ArrayList<>(); for (File srcDir : srcDirs) { String srcDirName = srcDir.getAbsolutePath(); // this is just to obtain pretty test names for testng tests List<String[]> a = Files.walk(Paths.get(srcDirName)) .map(Path::toFile) .map(File::getAbsolutePath) .filter(n -> n.endsWith(".java")) .map(n -> n.replace(srcDirName, "")) .map(n -> new String[]{n}) .collect(Collectors.toList()); if (a.isEmpty()) { throw new AssertionError("Java sources have not been found in directory: " + srcDirName); } list.addAll(a); } return list.toArray(new String[list.size()][]); } @Test(dataProvider = "crawler") public void testFile(String fileName) throws IOException { File file = getSourceFile(fileName); final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); final StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); boolean success = true; StringWriter writer = new StringWriter(); writer.write("Testing : " + file.toString() + "\n"); String text = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file); JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fileManager, null, null, null, compilationUnits); Iterable<? extends CompilationUnitTree> asts = task.parse(); Trees trees = Trees.instance(task); SourcePositions sp = trees.getSourcePositions(); for (CompilationUnitTree cut : asts) { for (ImportTree imp : cut.getImports()) { success &= testStatement(writer, sp, text, cut, imp); } for (Tree decl : cut.getTypeDecls()) { success &= testStatement(writer, sp, text, cut, decl); if (decl instanceof ClassTree) { ClassTree ct = (ClassTree) decl; for (Tree mem : ct.getMembers()) { if (mem instanceof MethodTree) { MethodTree mt = (MethodTree) mem; BlockTree bt = mt.getBody(); // No abstract methods or constructors if (bt != null && mt.getReturnType() != null) { // The modifiers synchronized, abstract, and default are not allowed on // top-level declarations and are errors. Set<Modifier> modifier = mt.getModifiers().getFlags(); if (!modifier.contains(Modifier.ABSTRACT) && !modifier.contains(Modifier.SYNCHRONIZED) && !modifier.contains(Modifier.DEFAULT)) { success &= testStatement(writer, sp, text, cut, mt); } testBlock(writer, sp, text, cut, bt); } } } } } } fileManager.close(); if (!success) { throw new AssertionError(writer.toString()); } } private boolean isLegal(StatementTree st) { return !(st instanceof ReturnTree) && !(st instanceof ContinueTree) && !(st instanceof BreakTree); } private boolean testBranch(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, StatementTree statementTree) { if (statementTree instanceof BlockTree) { return testBlock(writer, sp, text, cut, (BlockTree) statementTree); } else if (isLegal(statementTree)) { return testStatement(writer, sp, text, cut, statementTree); } return true; } private boolean testBlock(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, BlockTree blockTree) { boolean success = true; for (StatementTree st : blockTree.getStatements()) { if (isLegal(st)) { success &= testStatement(writer, sp, text, cut, st); } if (st instanceof IfTree) { IfTree ifTree = (IfTree) st; success &= testBranch(writer, sp, text, cut, ifTree.getThenStatement()); success &= testBranch(writer, sp, text, cut, ifTree.getElseStatement()); } else if (st instanceof WhileLoopTree) { WhileLoopTree whileLoopTree = (WhileLoopTree) st; success &= testBranch(writer, sp, text, cut, whileLoopTree.getStatement()); } else if (st instanceof DoWhileLoopTree) { DoWhileLoopTree doWhileLoopTree = (DoWhileLoopTree) st; success &= testBranch(writer, sp, text, cut, doWhileLoopTree.getStatement()); } else if (st instanceof ForLoopTree) { ForLoopTree forLoopTree = (ForLoopTree) st; success &= testBranch(writer, sp, text, cut, forLoopTree.getStatement()); } else if (st instanceof LabeledStatementTree) { LabeledStatementTree labelTree = (LabeledStatementTree) st; success &= testBranch(writer, sp, text, cut, labelTree.getStatement()); } else if (st instanceof SwitchTree) { SwitchTree switchTree = (SwitchTree) st; for (CaseTree caseTree : switchTree.getCases()) { for (StatementTree statementTree : caseTree.getStatements()) { success &= testBranch(writer, sp, text, cut, statementTree); } } } } return success; } private boolean testStatement(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, Tree statement) { if (statement == null) { return true; } int start = (int) sp.getStartPosition(cut, statement); int end = (int) sp.getEndPosition(cut, statement); char ch = text.charAt(end - 1); SourceCodeAnalysis.Completeness expected = COMPLETE; LineMap lineMap = cut.getLineMap(); int row = (int) lineMap.getLineNumber(start); int column = (int) lineMap.getColumnNumber(start); switch (ch) { case ',': case ';': expected = (statement instanceof ExpressionStatementTree) ? COMPLETE : COMPLETE_WITH_SEMI; --end; break; case '}': break; default: writer.write(String.format("Unexpected end: row %d, column %d: '%c' -- %s\n", row, column, ch, text.substring(start, end))); return true; } String unit = text.substring(start, end); SourceCodeAnalysis.CompletionInfo ci = getAnalysis().analyzeCompletion(unit); if (ci.completeness() != expected) { if (expected == COMPLETE_WITH_SEMI && (ci.completeness() == CONSIDERED_INCOMPLETE || ci.completeness() == EMPTY)) { writer.write(String.format("Empty statement: row %d, column %d: -- %s\n", start, end, unit)); } else { writer.write(String.format("Expected %s got %s: '%s' row %d, column %d: -- %s\n", expected, ci.completeness(), unit, row, column, unit)); return false; } } return true; } }