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