/*
* Copyright (c) 2015, 2021, 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.
*/
/*
* @test
* @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782 8235474 8246774 8276149
* @summary Test SourceCodeAnalysis
* @build KullaTesting TestingInputStream
* @run testng CompletenessTest
*/
import java.util.Map;
import java.util.HashMap;
import java.util.function .Consumer;
import javax.lang.model.SourceVersion;
import jdk.jshell.JShell;
import org.testng.annotations.Test;
import jdk.jshell.SourceCodeAnalysis.Completeness;
import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
import org.testng.annotations.BeforeMethod;
@Test
public class CompletenessTest extends KullaTesting {
// Add complete units that end with semicolon to complete_with_semi (without
// the semicolon). Both cases will be tested.
static final String[] complete = new String[] {
"{ x= 4; }" ,
"int mm(int x) {kll}" ,
"if (t) { ddd; }" ,
"for (int i = 0; i < lines.length(); ++i) { foo }" ,
"while (ct == null) { switch (current.kind) { case EOF: { } } }" ,
"if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \" Unmatched \" + unmatched); }" ,
"enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); }" ,
"List f() { return null; }" ,
"List> f() { return null; }" ,
"List extends Object> f() { return null; }" ,
"Map extends Object, ? super Object> f() { return null; }" ,
"class C { int z; }" ,
"synchronized (r) { f(); }" ,
"try { } catch (Exception ex) { }" ,
"try { } catch (Exception ex) { } finally { }" ,
"try { } finally { }" ,
"try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)) { }" ,
"foo: while (true) { printf(\" Innn\"); break foo; }" ,
"class Case, E2 extends Enum, E3 extends Enum> {}" ,
";" ,
"enum Tt { FOO, BAR, BAZ,; }" ,
"record D(int i) {}" ,
"static record D(int i) {}" ,
};
static final String[] expression = new String[] {
"test" ,
"x + y" ,
"x + y ++" ,
"p = 9" ,
"match(BRACKETS, TokenKind.LBRACKET)" ,
"new C()" ,
"new C() { public String toString() { return \" Hi\"; } }" ,
"new int[]" ,
"new int[] {1, 2,3}" ,
"new Foo() {}" ,
"i >= 0 && Character.isWhitespace(s.charAt(i))" ,
"int.class" ,
"String.class" ,
"record.any" ,
"record()" ,
"record(1)" ,
"record.length()"
};
static final String[] complete_with_semi = new String[] {
"int mm" ,
"if (t) ddd" ,
"int p = 9" ,
"int p" ,
"Deque stack = new ArrayDeque<>()" ,
"final Deque stack = new ArrayDeque<>()" ,
"java.util.Scanner input = new java.util.Scanner(System.in)" ,
"java.util.Scanner input = new java.util.Scanner(System.in) { }" ,
"int j = -i" ,
"String[] a = { \" AAA\" }" ,
"assert true" ,
"int path[]" ,
"int path[][]" ,
"int path[][] = new int[22][]" ,
"int path[] = new int[22]" ,
"int path[] = new int[] {1, 2, 3}" ,
"int[] path" ,
"int path[] = new int[22]" ,
"int path[][] = new int[22][]" ,
"for (Object o : a) System.out.println(\" Yep\")" ,
"while (os == null) System.out.println(\" Yep\")" ,
"do f(); while (t)" ,
"if (os == null) System.out.println(\" Yep\")" ,
"if (t) if (!t) System.out.println(123)" ,
"for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else break" ,
"for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else continue" ,
"for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else return" ,
"throw ex" ,
"C c = new C()" ,
"java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)" ,
"BufferedReader br = new BufferedReader(new FileReader(path))" ,
"bar: g()" ,
"baz: while (true) if (t()) printf('-'); else break baz" ,
"java.util.function.IntFunction ggg = int[]::new" ,
"List extends Object> l" ,
"int[] m = {1, 2}" ,
"int[] m = {1, 2}, n = null" ,
"int[] m = {1, 2}, n" ,
"int[] m = {1, 2}, n = {3, 4}" ,
};
static final String[] considered_incomplete = new String[] {
"if (t)" ,
"if (t) { } else" ,
"if (t) if (!t)" ,
"if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE))" ,
"for (int i = 0; i < 10; ++i)" ,
"while (os == null)" ,
};
static final String[] definitely_incomplete = new String[] {
"int mm(" ,
"int mm(int x" ,
"int mm(int x)" ,
"int mm(int x) {" ,
"int mm(int x) {kll" ,
"if" ,
"if (" ,
"if (t" ,
"if (t) {" ,
"if (t) { ddd" ,
"if (t) { ddd;" ,
"if (t) if (" ,
"if (stack.isEmpty()) {" ,
"if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) {" ,
"if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \" Unmatched \" + unmatched);" ,
"x +" ,
"x *" ,
"3 *" ,
"int" ,
"for (int i = 0; i < lines.length(); ++i) {" ,
"new" ,
"new C(" ,
"new int[" ,
"new int[] {1, 2,3" ,
"new int[] {" ,
"while (ct == null) {" ,
"while (ct == null) { switch (current.kind) {" ,
"while (ct == null) { switch (current.kind) { case EOF: {" ,
"while (ct == null) { switch (current.kind) { case EOF: { } }" ,
"enum TK {" ,
"enum TK { EOF(TokenKind.EOF, 0)," ,
"enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM)" ,
"enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); " ,
"enum Tt { FOO, BAR, BAZ,;" ,
"class C" ,
"class C extends D" ,
"class C implements D" ,
"class C implements D, E" ,
"interface I extends D" ,
"interface I extends D, E" ,
"enum E" ,
"enum E implements I1" ,
"enum E implements I1, I2" ,
"@interface Anno" ,
"void f()" ,
"void f() throws E" ,
"@A(" ,
"int n = 4," ,
"int n," ,
"int[] m = {1, 2}," ,
"int[] m = {1, 2}, n = {3, 4}," ,
"Map,
"switch (x) {" ,
"var v = switch (x) {" ,
"var v = switch (x) { case " ,
"var v = switch (x) { case 0:" ,
"var v = switch (x) { case 0: break 12; " ,
"record D" ,
"record D(" ,
"record D(String" ,
"record D(String i" ,
"record D(String i," ,
"record D(String i, String" ,
"record D(String i, String j" ,
"record D(String i)" ,
"record D(String i, String j)" ,
"record D(String i) {" ,
"record D(String i, String j) {" ,
"static record D" ,
"static record D(" ,
"static record D(String" ,
"static record D(String i" ,
"static record D(String i," ,
"static record D(String i, String" ,
"static record D(String i, String j" ,
"static record D(String i)" ,
"static record D(String i, String j)" ,
"static record D(String i) {" ,
"static record D(String i, String j) {" ,
//JDK-8276149:
"void t(int i) { int v = switch (i) { case 0 -> " ,
"void t(int i) { int v = switch (i) { case 0 -> {" ,
"void t(int i) { int v = switch (i) { case 0 -> a = b;" ,
"void t(int i) { int v = switch (i) { case 0 -> System.err.println(1);" ,
"void t(int i) { int v = switch (i) { case 0 -> throw new IllegalStateException();" ,
};
static final String[] unknown = new String[] {
"new ;"
};
static final Map<Completeness, String[]> statusToCases = new HashMap<>();
static {
statusToCases.put(COMPLETE, complete);
statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi);
statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete);
statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete);
}
private void assertStatus(String input, Completeness status, String source) {
String augSrc;
switch (status) {
case COMPLETE_WITH_SEMI:
augSrc = source + ";" ;
break ;
case DEFINITELY_INCOMPLETE:
augSrc = null ;
break ;
case CONSIDERED_INCOMPLETE:
augSrc = source + ";" ;
break ;
case EMPTY:
case COMPLETE:
case UNKNOWN:
augSrc = source;
break ;
default :
throw new AssertionError();
}
assertAnalyze(input, status, augSrc);
}
private void assertStatus(String[] ins, Completeness status) {
for (String input : ins) {
assertStatus(input, status, input);
}
}
public void test_complete() {
assertStatus(complete, COMPLETE);
}
public void test_expression() {
assertStatus(expression, COMPLETE);
}
public void test_complete_with_semi() {
assertStatus(complete_with_semi, COMPLETE_WITH_SEMI);
}
public void test_considered_incomplete() {
assertStatus(considered_incomplete, CONSIDERED_INCOMPLETE);
}
public void test_definitely_incomplete() {
assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
}
public void test_unknown() {
assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
}
public void testCompleted_complete_with_semi() {
for (String in : complete_with_semi) {
String input = in + ";" ;
assertStatus(input, COMPLETE, input);
}
}
public void testCompleted_expression_with_semi() {
for (String in : expression) {
String input = in + ";" ;
assertStatus(input, COMPLETE, input);
}
}
public void testCompleted_considered_incomplete() {
for (String in : considered_incomplete) {
String input = in + ";" ;
assertStatus(input, COMPLETE, input);
}
}
private void assertSourceByStatus(String first) {
for (Map.Entry<Completeness, String[]> e : statusToCases.entrySet()) {
for (String in : e.getValue()) {
String input = first + in;
assertAnalyze(input, COMPLETE, first, in, true );
}
}
}
public void testCompleteSource_complete() {
for (String input : complete) {
assertSourceByStatus(input);
}
}
public void testCompleteSource_complete_with_semi() {
for (String in : complete_with_semi) {
String input = in + ";" ;
assertSourceByStatus(input);
}
}
public void testCompleteSource_expression() {
for (String in : expression) {
String input = in + ";" ;
assertSourceByStatus(input);
}
}
public void testCompleteSource_considered_incomplete() {
for (String in : considered_incomplete) {
String input = in + ";" ;
assertSourceByStatus(input);
}
}
public void testTrailingSlash() {
assertStatus("\" abc\\", UNKNOWN, " \"abc\\" );
}
public void testOpenComment() {
assertStatus("int xx; /* hello", DEFINITELY_INCOMPLETE, null);
assertStatus("/** test", DEFINITELY_INCOMPLETE, null);
}
public void testTextBlocks() {
assertStatus("\" \"\" ", DEFINITELY_INCOMPLETE, null);
assertStatus("\" \"\" broken", DEFINITELY_INCOMPLETE, null);
assertStatus("\" \"\" \ntext", DEFINITELY_INCOMPLETE, null);
assertStatus("\" \"\" \ntext\"\" ", DEFINITELY_INCOMPLETE, " \"\" \"\ntext\" \"\" ");
assertStatus("\" \"\" \ntext\"\" \"" , COMPLETE, "\" \"\" \ntext\"\" \"" );
assertStatus("\" \"\" \ntext\\\"\" \"\" ", COMPLETE, " \"\" \"\ntext\\\" \"\" \"" );
assertStatus("\" \"\" \ntext\\\"\" \"" , DEFINITELY_INCOMPLETE, null );
assertStatus("\" \"\" \ntext\\\"\" \"\\\" \"\" ", DEFINITELY_INCOMPLETE, null);
assertStatus("\" \"\" \ntext\\\"\" \"\\\" \"\" \"\" \"" , COMPLETE, "\" \"\" \ntext\\\"\" \"\\\" \"\" \"\" \"" );
}
public void testMiscSource() {
assertStatus("if (t) if " , DEFINITELY_INCOMPLETE, "if (t) if" ); //Bug
assertStatus("int m() {} dfd" , COMPLETE, "int m() {}" );
assertStatus("int p = " , DEFINITELY_INCOMPLETE, "int p =" ); //Bug
assertStatus("int[] m = {1, 2}, n = new int[0]; int i;" , COMPLETE,
"int[] m = {1, 2}, n = new int[0];" );
}
}
quality 73%
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland