/*
* Copyright (c) 2015, 2016, 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
* @summary Test SourceCodeAnalysis
* @build KullaTesting TestingInputStream
* @run testng CompletenessTest
*/
import java.util.Map;
import java.util.HashMap;
import org.testng.annotations.Test;
import jdk.jshell.SourceCodeAnalysis.Completeness;
import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
@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<T> 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<E1 extends Enum<E1>, E2 extends Enum<E2>, E3 extends Enum<E3>> {}",
";",
"enum Tt { FOO, BAR, BAZ,; }"
};
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",
};
static final String[] complete_with_semi = new String[] {
"int mm",
"if (t) ddd",
"int p = 9",
"int p",
"Deque<Token> stack = new ArrayDeque<>()",
"final Deque<Token> 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<int[]> ggg = int[]::new",
};
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,;"
};
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:
case CONSIDERED_INCOMPLETE:
augSrc = null;
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 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
}
}