CompletenessStressTest.java revision 3062:15bdc18525ff
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
24import java.io.File;
25import java.io.IOException;
26import java.io.StringWriter;
27import java.nio.charset.StandardCharsets;
28import java.nio.file.Files;
29import java.nio.file.Path;
30import java.nio.file.Paths;
31import java.util.ArrayList;
32import java.util.List;
33import java.util.Set;
34import java.util.stream.Collectors;
35
36import javax.lang.model.element.Modifier;
37import javax.tools.JavaCompiler;
38import javax.tools.JavaFileObject;
39import javax.tools.StandardJavaFileManager;
40import javax.tools.ToolProvider;
41
42import com.sun.source.tree.BlockTree;
43import com.sun.source.tree.BreakTree;
44import com.sun.source.tree.CaseTree;
45import com.sun.source.tree.ClassTree;
46import com.sun.source.tree.CompilationUnitTree;
47import com.sun.source.tree.ContinueTree;
48import com.sun.source.tree.DoWhileLoopTree;
49import com.sun.source.tree.ExpressionStatementTree;
50import com.sun.source.tree.ForLoopTree;
51import com.sun.source.tree.IfTree;
52import com.sun.source.tree.ImportTree;
53import com.sun.source.tree.LabeledStatementTree;
54import com.sun.source.tree.LineMap;
55import com.sun.source.tree.MethodTree;
56import com.sun.source.tree.ReturnTree;
57import com.sun.source.tree.StatementTree;
58import com.sun.source.tree.SwitchTree;
59import com.sun.source.tree.Tree;
60import com.sun.source.tree.WhileLoopTree;
61import com.sun.source.util.SourcePositions;
62import com.sun.source.util.Trees;
63import com.sun.tools.javac.api.JavacTaskImpl;
64
65import jdk.jshell.SourceCodeAnalysis;
66
67import org.testng.annotations.DataProvider;
68import org.testng.annotations.Test;
69
70import static java.lang.Integer.max;
71import static java.lang.Integer.min;
72import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
73
74public 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
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
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")
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")
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
175    private boolean isLegal(StatementTree st) {
176        return !(st instanceof ReturnTree) &&
177                !(st instanceof ContinueTree) && !(st instanceof BreakTree);
178    }
179
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
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
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                String oops = unit.substring(max(0, ci.unitEndPos - 10), ci.unitEndPos) + "|||" +
257                        unit.substring(ci.unitEndPos, min(unit.length(), ci.unitEndPos + 10));
258                writer.write(String.format("Expected %s got %s: '%s'  row %d, column %d: -- %s\n",
259                        expected, ci.completeness, oops, row, column, unit));
260                return false;
261            }
262        }
263        return true;
264    }
265}
266