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