3789983e89
Reviewed-by: darcy, ihse
336 lines
12 KiB
Java
336 lines
12 KiB
Java
/*
|
|
* Copyright (c) 2014, 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 8065360
|
|
* @summary The test checks possibility of class members to be imported.
|
|
* @library /tools/lib
|
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
|
* jdk.compiler/com.sun.tools.javac.main
|
|
* jdk.jdeps/com.sun.tools.javap
|
|
* @build toolbox.ToolBox ImportMembersTest
|
|
* @run main ImportMembersTest
|
|
*/
|
|
|
|
import javax.tools.JavaCompiler;
|
|
import javax.tools.ToolProvider;
|
|
import java.io.StringWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
import toolbox.ToolBox;
|
|
|
|
/**
|
|
* The test checks that members of a class, an enum, an interface or annotation
|
|
* can be imported with help of a static import or an import statement.
|
|
* The tests generates a code, compiles it and checks whether it can be compiled
|
|
* successfully or fails with a proper message.
|
|
* The following is the example of a test case:
|
|
* package pkg;
|
|
* class ChildA extends A {}
|
|
*
|
|
* package pkg;
|
|
* class A {
|
|
* static class Inner {}
|
|
* static Object field;
|
|
* static void method() {}
|
|
* }
|
|
*
|
|
* package pkg;
|
|
* import static pkg.ChildA.method;
|
|
* public class Test {{
|
|
* method();
|
|
* }}
|
|
*
|
|
*/
|
|
public class ImportMembersTest {
|
|
|
|
private static final String[] expectedErrorMessages = {
|
|
"Test.java:\\d+:\\d+: compiler.err.cant.resolve.location: .*\n1 error\n",
|
|
"Test.java:\\d+:\\d+: compiler.err.import.requires.canonical: .*\n1 error\n"
|
|
};
|
|
|
|
private static final String sourceTemplate =
|
|
"package pkg;\n" +
|
|
"#IMPORT\n" +
|
|
"public class Test {{\n" +
|
|
" #STATEMENT\n" +
|
|
"}}\n";
|
|
|
|
public static void main(String[] args) {
|
|
new ImportMembersTest().test();
|
|
}
|
|
|
|
public void test() {
|
|
int passed = 0;
|
|
int total = 0;
|
|
for (ClassType classType : ClassType.values()) {
|
|
for (ImportType importType : ImportType.values()) {
|
|
for (MemberType memberType : MemberType.values()) {
|
|
++total;
|
|
List<ToolBox.JavaSource> sources = classType.getSources();
|
|
sources.add(new ToolBox.JavaSource("Test.java",
|
|
generateSource(classType, memberType, importType)));
|
|
|
|
CompilationResult compilationResult = compile(sources);
|
|
boolean isErrorExpected = importType.hasError(classType, memberType);
|
|
if (!compilationResult.isSuccessful) {
|
|
if (isErrorExpected) {
|
|
String expectedErrorMessage =
|
|
getExpectedErrorMessage(classType, importType, memberType);
|
|
if (compilationResult.message.matches(expectedErrorMessage)) {
|
|
++passed;
|
|
} else {
|
|
reportFailure(sources, String.format("Expected compilation failure message:\n" +
|
|
"%s\ngot message:\n%s",
|
|
expectedErrorMessage, compilationResult.message));
|
|
}
|
|
} else {
|
|
reportFailure(sources, String.format("Unexpected compilation failure:\n%s",
|
|
compilationResult.message));
|
|
}
|
|
} else {
|
|
if (isErrorExpected) {
|
|
reportFailure(sources, "Expected compilation failure.");
|
|
} else {
|
|
++passed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
String message = String.format(
|
|
"Total test cases run: %d, passed: %d, failed: %d.",
|
|
total, passed, total - passed);
|
|
if (passed != total) {
|
|
throw new RuntimeException(message);
|
|
}
|
|
echo(message);
|
|
}
|
|
|
|
private String getExpectedErrorMessage(ClassType classType, ImportType importType, MemberType memberType) {
|
|
String expectedErrorMessage;
|
|
if (importType == ImportType.IMPORT && classType == ClassType.CHILD_A &&
|
|
memberType == MemberType.CLASS) {
|
|
expectedErrorMessage = expectedErrorMessages[1];
|
|
} else {
|
|
expectedErrorMessage = expectedErrorMessages[0];
|
|
}
|
|
return expectedErrorMessage;
|
|
}
|
|
|
|
private void reportFailure(List<ToolBox.JavaSource> sources, String message) {
|
|
echo("Test case failed!");
|
|
printSources(sources);
|
|
echo(message);
|
|
echo();
|
|
}
|
|
|
|
private String generateSource(ClassType classType, MemberType memberType, ImportType importType) {
|
|
String importString = importType.generateImport(classType.getClassName(), memberType.getMemberType());
|
|
String statement;
|
|
if (importType.hasError(classType, memberType)) {
|
|
// if the source code has a compilation error, nothing is added.
|
|
// just to prevent the compiler from appending additional
|
|
// compilation errors to output
|
|
statement = "";
|
|
} else if (memberType == MemberType.STAR) {
|
|
// in case of import-on-demand, every class member is used
|
|
if (importType == ImportType.STATIC_IMPORT) {
|
|
statement = MemberType.CLASS.getStatement() + "\n "
|
|
+ MemberType.FIELD.getStatement();
|
|
// an annotation does not have a static method.
|
|
if (classType != ClassType.D) {
|
|
statement += "\n " + MemberType.METHOD.getStatement() + "\n";
|
|
}
|
|
} else {
|
|
statement = classType != ClassType.CHILD_A
|
|
? MemberType.CLASS.getStatement() : "";
|
|
}
|
|
} else {
|
|
statement = memberType.getStatement();
|
|
}
|
|
return sourceTemplate
|
|
.replace("#IMPORT", importString)
|
|
.replace("#STATEMENT", statement);
|
|
}
|
|
|
|
private static class CompilationResult {
|
|
public final boolean isSuccessful;
|
|
public final String message;
|
|
|
|
public CompilationResult(boolean isSuccessful, String message) {
|
|
this.isSuccessful = isSuccessful;
|
|
this.message = message;
|
|
}
|
|
}
|
|
|
|
private CompilationResult compile(List<ToolBox.JavaSource> sources) {
|
|
StringWriter writer = new StringWriter();
|
|
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
|
|
Boolean call = jc.getTask(writer, null, null, Arrays.asList("-XDrawDiagnostics"), null, sources).call();
|
|
return new CompilationResult(call, writer.toString().replace(ToolBox.lineSeparator, "\n"));
|
|
}
|
|
|
|
public void printSources(List<ToolBox.JavaSource> sources) {
|
|
for (ToolBox.JavaSource javaSource : sources) {
|
|
echo(javaSource.getCharContent(true).toString());
|
|
}
|
|
}
|
|
|
|
public void echo() {
|
|
echo("");
|
|
}
|
|
|
|
public void echo(String output) {
|
|
printf(output + "\n");
|
|
}
|
|
|
|
public void printf(String template, Object...args) {
|
|
System.err.print(String.format(template, args).replace("\n", ToolBox.lineSeparator));
|
|
}
|
|
|
|
enum ClassType {
|
|
A("A",
|
|
"package pkg;\n" +
|
|
"class A {\n" +
|
|
" static class Inner {}\n" +
|
|
" static Object field;\n" +
|
|
" static void method() {}\n" +
|
|
"}\n"
|
|
),
|
|
B("B",
|
|
"package pkg;\n" +
|
|
"interface B {\n" +
|
|
" static class Inner {}\n" +
|
|
" static Object field = null;\n" +
|
|
" static void method() {}\n" +
|
|
"}\n"
|
|
),
|
|
C("C",
|
|
"package pkg;\n" +
|
|
"enum C {field;\n" +
|
|
" static class Inner {}\n" +
|
|
" static void method() {}\n" +
|
|
"}\n"
|
|
),
|
|
D("D",
|
|
"package pkg;\n" +
|
|
"@interface D {\n" +
|
|
" static class Inner {}\n" +
|
|
" static Object field = null;\n" +
|
|
"}\n"
|
|
),
|
|
CHILD_A("ChildA",
|
|
"package pkg;\n" +
|
|
"class ChildA extends A {}\n",
|
|
A);
|
|
|
|
private final String className;
|
|
private final String source;
|
|
private final ClassType parentType;
|
|
|
|
private ClassType(String className, String source) {
|
|
this(className, source, null);
|
|
}
|
|
|
|
private ClassType(String className, String source, ClassType classType) {
|
|
this.className = className;
|
|
this.source = source;
|
|
this.parentType = classType;
|
|
}
|
|
|
|
public String getClassName() {
|
|
return className;
|
|
}
|
|
|
|
public List<ToolBox.JavaSource> getSources() {
|
|
List<ToolBox.JavaSource> sourceList = new ArrayList<>();
|
|
ClassType current = this;
|
|
while (current != null) {
|
|
sourceList.add(new ToolBox.JavaSource(current.className, current.source));
|
|
current = current.parentType;
|
|
}
|
|
return sourceList;
|
|
}
|
|
}
|
|
|
|
enum MemberType {
|
|
CLASS("Inner", "Inner inner = null;"),
|
|
FIELD("field", "Object o = field;"),
|
|
METHOD("method", "method();"),
|
|
STAR("*", ""),
|
|
NOT_EXIST("NotExist", "");
|
|
|
|
private final String memberType;
|
|
private final String statement;
|
|
|
|
private MemberType(String memberType, String statement) {
|
|
this.memberType = memberType;
|
|
this.statement = statement;
|
|
}
|
|
|
|
public String getStatement() {
|
|
return statement;
|
|
}
|
|
|
|
public String getMemberType() {
|
|
return memberType;
|
|
}
|
|
}
|
|
|
|
enum ImportType {
|
|
IMPORT("import pkg.#CLASS_NAME.#MEMBER_NAME;"),
|
|
STATIC_IMPORT("import static pkg.#CLASS_NAME.#MEMBER_NAME;");
|
|
|
|
private final String importType;
|
|
|
|
private ImportType(String importType) {
|
|
this.importType = importType;
|
|
}
|
|
|
|
public String generateImport(String className, String memberName) {
|
|
return importType
|
|
.replace("#CLASS_NAME", className)
|
|
.replace("#MEMBER_NAME", memberName);
|
|
}
|
|
|
|
public boolean hasError(ClassType classType, MemberType memberType) {
|
|
switch (memberType) {
|
|
case FIELD:
|
|
return this != ImportType.STATIC_IMPORT;
|
|
case METHOD:
|
|
return this != ImportType.STATIC_IMPORT || classType == ClassType.D;
|
|
case NOT_EXIST:
|
|
return true;
|
|
case CLASS:
|
|
return classType.parentType != null && this != STATIC_IMPORT;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|