| /* |
| * 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; |
| } |
| } |