ImportMembersTest.java revision 3294:9adfb22ff08f
1/* 2 * Copyright (c) 2014, 2016, 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 24/* 25 * @test 26 * @bug 8065360 27 * @summary The test checks possibility of class members to be imported. 28 * @library /tools/lib 29 * @modules jdk.compiler/com.sun.tools.javac.api 30 * jdk.compiler/com.sun.tools.javac.file 31 * jdk.compiler/com.sun.tools.javac.main 32 * jdk.jdeps/com.sun.tools.javap 33 * @build ToolBox ImportMembersTest 34 * @run main ImportMembersTest 35 */ 36 37import javax.tools.JavaCompiler; 38import javax.tools.ToolProvider; 39import java.io.StringWriter; 40import java.util.ArrayList; 41import java.util.Arrays; 42import java.util.List; 43 44/** 45 * The test checks that members of a class, an enum, an interface or annotation 46 * can be imported with help of a static import or an import statement. 47 * The tests generates a code, compiles it and checks whether it can be compiled 48 * successfully or fails with a proper message. 49 * The following is the example of a test case: 50 * package pkg; 51 * class ChildA extends A {} 52 * 53 * package pkg; 54 * class A { 55 * static class Inner {} 56 * static Object field; 57 * static void method() {} 58 * } 59 * 60 * package pkg; 61 * import static pkg.ChildA.method; 62 * public class Test {{ 63 * method(); 64 * }} 65 * 66 */ 67public class ImportMembersTest { 68 69 private static final String[] expectedErrorMessages = { 70 "Test.java:\\d+:\\d+: compiler.err.cant.resolve.location: .*\n1 error\n", 71 "Test.java:\\d+:\\d+: compiler.err.import.requires.canonical: .*\n1 error\n" 72 }; 73 74 private static final String sourceTemplate = 75 "package pkg;\n" + 76 "#IMPORT\n" + 77 "public class Test {{\n" + 78 " #STATEMENT\n" + 79 "}}\n"; 80 81 public static void main(String[] args) { 82 new ImportMembersTest().test(); 83 } 84 85 public void test() { 86 int passed = 0; 87 int total = 0; 88 for (ClassType classType : ClassType.values()) { 89 for (ImportType importType : ImportType.values()) { 90 for (MemberType memberType : MemberType.values()) { 91 ++total; 92 List<ToolBox.JavaSource> sources = classType.getSources(); 93 sources.add(new ToolBox.JavaSource("Test.java", 94 generateSource(classType, memberType, importType))); 95 96 CompilationResult compilationResult = compile(sources); 97 boolean isErrorExpected = importType.hasError(classType, memberType); 98 if (!compilationResult.isSuccessful) { 99 if (isErrorExpected) { 100 String expectedErrorMessage = 101 getExpectedErrorMessage(classType, importType, memberType); 102 if (compilationResult.message.matches(expectedErrorMessage)) { 103 ++passed; 104 } else { 105 reportFailure(sources, String.format("Expected compilation failure message:\n" + 106 "%s\ngot message:\n%s", 107 expectedErrorMessage, compilationResult.message)); 108 } 109 } else { 110 reportFailure(sources, String.format("Unexpected compilation failure:\n%s", 111 compilationResult.message)); 112 } 113 } else { 114 if (isErrorExpected) { 115 reportFailure(sources, "Expected compilation failure."); 116 } else { 117 ++passed; 118 } 119 } 120 } 121 } 122 } 123 String message = String.format( 124 "Total test cases run: %d, passed: %d, failed: %d.", 125 total, passed, total - passed); 126 if (passed != total) { 127 throw new RuntimeException(message); 128 } 129 echo(message); 130 } 131 132 private String getExpectedErrorMessage(ClassType classType, ImportType importType, MemberType memberType) { 133 String expectedErrorMessage; 134 if (importType == ImportType.IMPORT && classType == ClassType.CHILD_A && 135 memberType == MemberType.CLASS) { 136 expectedErrorMessage = expectedErrorMessages[1]; 137 } else { 138 expectedErrorMessage = expectedErrorMessages[0]; 139 } 140 return expectedErrorMessage; 141 } 142 143 private void reportFailure(List<ToolBox.JavaSource> sources, String message) { 144 echo("Test case failed!"); 145 printSources(sources); 146 echo(message); 147 echo(); 148 } 149 150 private String generateSource(ClassType classType, MemberType memberType, ImportType importType) { 151 String importString = importType.generateImport(classType.getClassName(), memberType.getMemberType()); 152 String statement; 153 if (importType.hasError(classType, memberType)) { 154 // if the source code has a compilation error, nothing is added. 155 // just to prevent the compiler from appending additional 156 // compilation errors to output 157 statement = ""; 158 } else if (memberType == MemberType.STAR) { 159 // in case of import-on-demand, every class member is used 160 if (importType == ImportType.STATIC_IMPORT) { 161 statement = MemberType.CLASS.getStatement() + "\n " 162 + MemberType.FIELD.getStatement(); 163 // an annotation does not have a static method. 164 if (classType != ClassType.D) { 165 statement += "\n " + MemberType.METHOD.getStatement() + "\n"; 166 } 167 } else { 168 statement = classType != ClassType.CHILD_A 169 ? MemberType.CLASS.getStatement() : ""; 170 } 171 } else { 172 statement = memberType.getStatement(); 173 } 174 return sourceTemplate 175 .replace("#IMPORT", importString) 176 .replace("#STATEMENT", statement); 177 } 178 179 private static class CompilationResult { 180 public final boolean isSuccessful; 181 public final String message; 182 183 public CompilationResult(boolean isSuccessful, String message) { 184 this.isSuccessful = isSuccessful; 185 this.message = message; 186 } 187 } 188 189 private CompilationResult compile(List<ToolBox.JavaSource> sources) { 190 StringWriter writer = new StringWriter(); 191 JavaCompiler jc = ToolProvider.getSystemJavaCompiler(); 192 Boolean call = jc.getTask(writer, null, null, Arrays.asList("-XDrawDiagnostics"), null, sources).call(); 193 return new CompilationResult(call, writer.toString().replace(ToolBox.lineSeparator, "\n")); 194 } 195 196 public void printSources(List<ToolBox.JavaSource> sources) { 197 for (ToolBox.JavaSource javaSource : sources) { 198 echo(javaSource.getCharContent(true).toString()); 199 } 200 } 201 202 public void echo() { 203 echo(""); 204 } 205 206 public void echo(String output) { 207 printf(output + "\n"); 208 } 209 210 public void printf(String template, Object...args) { 211 System.err.print(String.format(template, args).replace("\n", ToolBox.lineSeparator)); 212 } 213 214 enum ClassType { 215 A("A", 216 "package pkg;\n" + 217 "class A {\n" + 218 " static class Inner {}\n" + 219 " static Object field;\n" + 220 " static void method() {}\n" + 221 "}\n" 222 ), 223 B("B", 224 "package pkg;\n" + 225 "interface B {\n" + 226 " static class Inner {}\n" + 227 " static Object field = null;\n" + 228 " static void method() {}\n" + 229 "}\n" 230 ), 231 C("C", 232 "package pkg;\n" + 233 "enum C {field;\n" + 234 " static class Inner {}\n" + 235 " static void method() {}\n" + 236 "}\n" 237 ), 238 D("D", 239 "package pkg;\n" + 240 "@interface D {\n" + 241 " static class Inner {}\n" + 242 " static Object field = null;\n" + 243 "}\n" 244 ), 245 CHILD_A("ChildA", 246 "package pkg;\n" + 247 "class ChildA extends A {}\n", 248 A); 249 250 private final String className; 251 private final String source; 252 private final ClassType parentType; 253 254 private ClassType(String className, String source) { 255 this(className, source, null); 256 } 257 258 private ClassType(String className, String source, ClassType classType) { 259 this.className = className; 260 this.source = source; 261 this.parentType = classType; 262 } 263 264 public String getClassName() { 265 return className; 266 } 267 268 public List<ToolBox.JavaSource> getSources() { 269 List<ToolBox.JavaSource> sourceList = new ArrayList<>(); 270 ClassType current = this; 271 while (current != null) { 272 sourceList.add(new ToolBox.JavaSource(current.className, current.source)); 273 current = current.parentType; 274 } 275 return sourceList; 276 } 277 } 278 279 enum MemberType { 280 CLASS("Inner", "Inner inner = null;"), 281 FIELD("field", "Object o = field;"), 282 METHOD("method", "method();"), 283 STAR("*", ""), 284 NOT_EXIST("NotExist", ""); 285 286 private final String memberType; 287 private final String statement; 288 289 private MemberType(String memberType, String statement) { 290 this.memberType = memberType; 291 this.statement = statement; 292 } 293 294 public String getStatement() { 295 return statement; 296 } 297 298 public String getMemberType() { 299 return memberType; 300 } 301 } 302 303 enum ImportType { 304 IMPORT("import pkg.#CLASS_NAME.#MEMBER_NAME;"), 305 STATIC_IMPORT("import static pkg.#CLASS_NAME.#MEMBER_NAME;"); 306 307 private final String importType; 308 309 private ImportType(String importType) { 310 this.importType = importType; 311 } 312 313 public String generateImport(String className, String memberName) { 314 return importType 315 .replace("#CLASS_NAME", className) 316 .replace("#MEMBER_NAME", memberName); 317 } 318 319 public boolean hasError(ClassType classType, MemberType memberType) { 320 switch (memberType) { 321 case FIELD: 322 return this != ImportType.STATIC_IMPORT; 323 case METHOD: 324 return this != ImportType.STATIC_IMPORT || classType == ClassType.D; 325 case NOT_EXIST: 326 return true; 327 case CLASS: 328 return classType.parentType != null && this != STATIC_IMPORT; 329 default: 330 return false; 331 } 332 } 333 } 334} 335