diff -r 4ebc2e2fb97c -r 71c04702a3d5 test/langtools/jdk/jshell/CompletenessStressTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/jdk/jshell/CompletenessStressTest.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,263 @@ +/* + * 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 list = new ArrayList<>(); + for (File srcDir : srcDirs) { + String srcDirName = srcDir.getAbsolutePath(); + // this is just to obtain pretty test names for testng tests + List 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 compilationUnits = fileManager.getJavaFileObjects(file); + JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fileManager, null, null, null, compilationUnits); + Iterable 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 = 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; + } +}