ClassMembersTest.java revision 3062:15bdc18525ff
1/* 2 * Copyright (c) 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 * @summary Test access to members of user defined class. 27 * @build KullaTesting TestingInputStream ExpectedDiagnostic 28 * @run testng/timeout=600 ClassMembersTest 29 */ 30 31import java.lang.annotation.RetentionPolicy; 32import java.util.ArrayList; 33import java.util.List; 34 35import javax.tools.Diagnostic; 36 37import jdk.jshell.SourceCodeAnalysis; 38import org.testng.annotations.DataProvider; 39import org.testng.annotations.Test; 40 41public class ClassMembersTest extends KullaTesting { 42 43 @Test(dataProvider = "memberTestCase") 44 public void memberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) { 45 MemberTestCase testCase = new MemberTestCase(accessModifier, codeChunk, isStaticMember, isStaticReference); 46 assertEval(testCase.generateSource()); 47 String expectedMessage = testCase.expectedMessage; 48 if (testCase.codeChunk != CodeChunk.CONSTRUCTOR || testCase.isAccessible()) { 49 assertEval("A a = new A();"); 50 } 51 if (expectedMessage == null) { 52 assertEval(testCase.useCodeChunk()); 53 } else { 54 assertDeclareFail(testCase.useCodeChunk(), expectedMessage); 55 } 56 } 57 58 private List<String> parseCode(String input) { 59 List<String> list = new ArrayList<>(); 60 SourceCodeAnalysis codeAnalysis = getAnalysis(); 61 String source = input; 62 while (!source.trim().isEmpty()) { 63 SourceCodeAnalysis.CompletionInfo info = codeAnalysis.analyzeCompletion(source); 64 list.add(info.source); 65 source = info.remaining; 66 } 67 return list; 68 } 69 70 @Test(dataProvider = "memberTestCase") 71 public void extendsMemberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) { 72 MemberTestCase testCase = new ExtendsMemberTestCase(accessModifier, codeChunk, isStaticMember, isStaticReference); 73 String input = testCase.generateSource(); 74 List<String> ss = parseCode(input); 75 assertEval(ss.get(0)); 76 if (testCase.codeChunk != CodeChunk.CONSTRUCTOR || testCase.isAccessible()) { 77 assertEval(ss.get(1)); 78 assertEval("B b = new B();"); 79 } 80 String expectedMessage = testCase.expectedMessage; 81 if (expectedMessage == null) { 82 assertEval(testCase.useCodeChunk()); 83 } else { 84 assertDeclareFail(testCase.useCodeChunk(), expectedMessage); 85 } 86 } 87 88 @Test 89 public void interfaceTest() { 90 String interfaceSource = 91 "interface A {\n" + 92 " default int defaultMethod() { return 1; }\n" + 93 " static int staticMethod() { return 2; }\n" + 94 " int method();\n" + 95 " class Inner1 {}\n" + 96 " static class Inner2 {}\n" + 97 "}"; 98 assertEval(interfaceSource); 99 assertEval("A.staticMethod();", "2"); 100 String classSource = 101 "class B implements A {\n" + 102 " public int method() { return 3; }\n" + 103 "}"; 104 assertEval(classSource); 105 assertEval("B b = new B();"); 106 assertEval("b.defaultMethod();", "1"); 107 assertDeclareFail("B.staticMethod();", 108 new ExpectedDiagnostic("compiler.err.cant.resolve.location.args", 0, 14, 1, -1, -1, Diagnostic.Kind.ERROR)); 109 assertEval("b.method();", "3"); 110 assertEval("new A.Inner1();"); 111 assertEval("new A.Inner2();"); 112 assertEval("new B.Inner1();"); 113 assertEval("new B.Inner2();"); 114 } 115 116 @Test 117 public void enumTest() { 118 String enumSource = 119 "enum E {A(\"s\");\n" + 120 " private final String s;\n" + 121 " private E(String s) { this.s = s; }\n" + 122 " public String method() { return s; }\n" + 123 " private String privateMethod() { return s; }\n" + 124 " public static String staticMethod() { return staticPrivateMethod(); }\n" + 125 " private static String staticPrivateMethod() { return \"a\"; }\n" + 126 "}"; 127 assertEval(enumSource); 128 assertEval("E a = E.A;", "A"); 129 assertDeclareFail("a.s;", 130 new ExpectedDiagnostic("compiler.err.report.access", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR)); 131 assertDeclareFail("new E(\"q\");", 132 new ExpectedDiagnostic("compiler.err.enum.cant.be.instantiated", 0, 10, 0, -1, -1, Diagnostic.Kind.ERROR)); 133 assertEval("a.method();", "\"s\""); 134 assertDeclareFail("a.privateMethod();", 135 new ExpectedDiagnostic("compiler.err.report.access", 0, 15, 1, -1, -1, Diagnostic.Kind.ERROR)); 136 assertEval("E.staticMethod();", "\"a\""); 137 assertDeclareFail("a.staticPrivateMethod();", 138 new ExpectedDiagnostic("compiler.err.report.access", 0, 21, 1, -1, -1, Diagnostic.Kind.ERROR)); 139 assertDeclareFail("E.method();", 140 new ExpectedDiagnostic("compiler.err.non-static.cant.be.ref", 0, 8, 1, -1, -1, Diagnostic.Kind.ERROR)); 141 } 142 143 @Test(enabled = false) // TODO 8080354 144 public void annotationTest() { 145 assertEval("import java.lang.annotation.*;"); 146 for (RetentionPolicy policy : RetentionPolicy.values()) { 147 String annotationSource = 148 "@Retention(RetentionPolicy." + policy.toString() + ")\n" + 149 "@interface A {}"; 150 assertEval(annotationSource); 151 String classSource = 152 "@A class C {\n" + 153 " @A C() {}\n" + 154 " @A void f() {}\n" + 155 " @A int f;\n" + 156 " @A class Inner {}\n" + 157 "}"; 158 assertEval(classSource); 159 String isRuntimeVisible = policy == RetentionPolicy.RUNTIME ? "true" : "false"; 160 assertEval("C.class.getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 161 assertEval("C.class.getDeclaredConstructor().getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 162 assertEval("C.class.getDeclaredMethod(\"f\").getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 163 assertEval("C.class.getDeclaredField(\"f\").getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 164 assertEval("C.Inner.class.getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 165 } 166 } 167 168 @DataProvider(name = "memberTestCase") 169 public Object[][] memberTestCaseGenerator() { 170 List<Object[]> list = new ArrayList<>(); 171 for (AccessModifier accessModifier : AccessModifier.values()) { 172 for (Static isStaticMember : Static.values()) { 173 for (Static isStaticReference : Static.values()) { 174 for (CodeChunk codeChunk : CodeChunk.values()) { 175 if (codeChunk == CodeChunk.CONSTRUCTOR && isStaticMember == Static.STATIC) { 176 continue; 177 } 178 list.add(new Object[]{ accessModifier, codeChunk, isStaticMember, isStaticReference }); 179 } 180 } 181 } 182 } 183 return list.toArray(new Object[list.size()][]); 184 } 185 186 public static class ExtendsMemberTestCase extends MemberTestCase { 187 188 public ExtendsMemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) { 189 super(accessModifier, codeChunk, isStaticMember, isStaticReference); 190 } 191 192 @Override 193 public String getSourceTemplate() { 194 return super.getSourceTemplate() + "\n" 195 + "class B extends A {}"; 196 } 197 198 @Override 199 public String errorMessage() { 200 if (!isAccessible()) { 201 if (codeChunk == CodeChunk.METHOD) { 202 return "compiler.err.cant.resolve.location.args"; 203 } 204 if (codeChunk == CodeChunk.CONSTRUCTOR) { 205 return "compiler.err.cant.resolve.location"; 206 } 207 } 208 return super.errorMessage(); 209 } 210 211 @Override 212 public String useCodeChunk() { 213 return useCodeChunk("B"); 214 } 215 } 216 217 public static class MemberTestCase { 218 public final AccessModifier accessModifier; 219 public final CodeChunk codeChunk; 220 public final Static isStaticMember; 221 public final Static isStaticReference; 222 public final String expectedMessage; 223 224 public MemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, 225 Static isStaticReference) { 226 this.accessModifier = accessModifier; 227 this.codeChunk = codeChunk; 228 this.isStaticMember = isStaticMember; 229 this.isStaticReference = isStaticReference; 230 this.expectedMessage = errorMessage(); 231 } 232 233 public String getSourceTemplate() { 234 return "class A {\n" + 235 " #MEMBER#\n" + 236 "}"; 237 } 238 239 public boolean isAccessible() { 240 return accessModifier != AccessModifier.PRIVATE; 241 } 242 243 public String errorMessage() { 244 if (!isAccessible()) { 245 return "compiler.err.report.access"; 246 } 247 if (codeChunk == CodeChunk.INNER_INTERFACE) { 248 return "compiler.err.abstract.cant.be.instantiated"; 249 } 250 if (isStaticMember == Static.STATIC) { 251 if (isStaticReference == Static.NO && codeChunk == CodeChunk.INNER_CLASS) { 252 return "compiler.err.qualified.new.of.static.class"; 253 } 254 return null; 255 } 256 if (isStaticReference == Static.STATIC) { 257 if (codeChunk == CodeChunk.CONSTRUCTOR) { 258 return null; 259 } 260 if (codeChunk == CodeChunk.INNER_CLASS) { 261 return "compiler.err.encl.class.required"; 262 } 263 return "compiler.err.non-static.cant.be.ref"; 264 } 265 return null; 266 } 267 268 public String generateSource() { 269 return getSourceTemplate().replace("#MEMBER#", codeChunk.generateSource(accessModifier, isStaticMember)); 270 } 271 272 protected String useCodeChunk(String className) { 273 String name = className.toLowerCase(); 274 switch (codeChunk) { 275 case CONSTRUCTOR: 276 return String.format("new %s();", className); 277 case METHOD: 278 if (isStaticReference == Static.STATIC) { 279 return String.format("%s.method();", className); 280 } else { 281 return String.format("%s.method();", name); 282 } 283 case FIELD: 284 if (isStaticReference == Static.STATIC) { 285 return String.format("%s.field;", className); 286 } else { 287 return String.format("%s.field;", name); 288 } 289 case INNER_CLASS: 290 if (isStaticReference == Static.STATIC) { 291 return String.format("new %s.Inner();", className); 292 } else { 293 return String.format("%s.new Inner();", name); 294 } 295 case INNER_INTERFACE: 296 return String.format("new %s.Inner();", className); 297 default: 298 throw new AssertionError("Unknown code chunk: " + this); 299 } 300 } 301 302 public String useCodeChunk() { 303 return useCodeChunk("A"); 304 } 305 } 306 307 public enum AccessModifier { 308 PUBLIC("public"), 309 PROTECTED("protected"), 310 PACKAGE_PRIVATE(""), 311 PRIVATE("private"); 312 313 private final String modifier; 314 315 AccessModifier(String modifier) { 316 this.modifier = modifier; 317 } 318 319 public String getModifier() { 320 return modifier; 321 } 322 } 323 324 public enum Static { 325 STATIC("static"), NO(""); 326 327 private final String modifier; 328 329 Static(String modifier) { 330 this.modifier = modifier; 331 } 332 333 public String getModifier() { 334 return modifier; 335 } 336 } 337 338 public enum CodeChunk { 339 CONSTRUCTOR("#MODIFIER# A() {}"), 340 METHOD("#MODIFIER# int method() { return 10; }"), 341 FIELD("#MODIFIER# int field = 10;"), 342 INNER_CLASS("#MODIFIER# class Inner {}"), 343 INNER_INTERFACE("#MODIFIER# interface Inner {}"); 344 345 private final String code; 346 347 CodeChunk(String code) { 348 this.code = code; 349 } 350 351 public String generateSource(AccessModifier accessModifier, Static isStatic) { 352 return code.replace("#MODIFIER#", accessModifier.getModifier() + " " + isStatic.getModifier()); 353 } 354 } 355} 356