changeset 56993:7f0d4f15e62b records-and-sealed

adding test RecordCompilationTests
author vromero
date Thu, 15 Aug 2019 19:02:11 -0400
parents 10f3b9bd83b8
children a44e7deb804c be8b67a276dc
files test/langtools/tools/javac/records/RecordCompilationTests.java
diffstat 1 files changed, 293 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/records/RecordCompilationTests.java	Thu Aug 15 19:02:11 2019 -0400
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.util.List;
+
+import org.testng.ITestResult;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import tools.javac.combo.JavacTemplateTestBase;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * RecordCompilationTests
+ *
+ * @test
+ * @summary Negative compilation tests, and positive compilation (smoke) tests for records
+ * @library /lib/combo
+ * @modules jdk.compiler/com.sun.tools.javac.util
+ * @run testng RecordCompilationTests
+ */
+@Test
+public class RecordCompilationTests extends JavacTemplateTestBase {
+
+    private static final List<String> BAD_COMPONENT_NAMES
+            = List.of("hashCode", "toString", "getClass",
+            "readObjectNoData", "readResolve", "writeReplace", "serialPersistentFields");
+
+    // @@@ When records become a permanent feature, we don't need these any more
+    private static String[] PREVIEW_OPTIONS = {"--enable-preview", "-source",
+            Integer.toString(Runtime.version().feature())};
+
+    @AfterMethod
+    public void dumpTemplateIfError(ITestResult result) {
+        // Make sure offending template ends up in log file on failure
+        if (!result.isSuccess()) {
+            System.err.printf("Diagnostics: %s%nTemplate: %s%n", diags.errorKeys(),
+                    sourceFiles.stream().map(p -> p.snd).collect(toList()));
+        }
+    }
+
+    private String expand(String... constructs) {
+        String s = "#";
+        for (String c : constructs)
+            s = s.replace("#", c);
+        return s;
+    }
+
+    private void assertCompile(String program, Runnable postTest) {
+        reset();
+        addCompileOptions(PREVIEW_OPTIONS);
+        addSourceFile("R.java", new StringTemplate(program));
+        try {
+            compile();
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        postTest.run();
+    }
+
+    private void assertOK(String... constructs) {
+        assertCompile(expand(constructs), this::assertCompileSucceeded);
+    }
+
+    private void assertOKWithWarning(String warning, String... constructs) {
+        assertCompile(expand(constructs), () -> assertCompileSucceededWithWarning(warning));
+    }
+
+    private void assertFail(String expectedDiag, String... constructs) {
+        assertCompile(expand(constructs), () -> assertCompileFailed(expectedDiag));
+    }
+
+    // -- Actual test cases start here --
+
+    public void testMalformedDeclarations() {
+        assertFail("compiler.err.premature.eof", "record R()");
+        assertFail("compiler.err.premature.eof", "record R();");
+        assertFail("compiler.err.illegal.start.of.type", "record R(,) { }");
+        assertFail("compiler.err.illegal.start.of.type", "record R((int x)) { }");
+        assertFail("compiler.err.expected", "record R(foo) { }");
+        assertFail("compiler.err.expected", "record R(int int) { }");
+        assertFail("compiler.err.restricted.type.not.allowed.here", "record R(var x) { }");
+        // @@@ assertFail("compiler.err.restricted.type.not.allowed.here", "record R(record x) { }");
+        assertFail("compiler.err.record.cant.declare.field.modifiers", "record R(public String foo) { }");
+        assertFail("compiler.err.record.cant.declare.field.modifiers", "record R(private String foo) { }");
+        // @@@ Duplicates RecordCantBeAbstractTest
+        assertFail("compiler.err.mod.not.allowed.here", "abstract record R(String foo) { }");
+        assertFail("compiler.err.illegal.combination.of.modifiers", "non-sealed record R(String foo) { }");
+        assertFail("compiler.err.repeated.modifier", "public public record R(String foo) { }");
+        assertFail("compiler.err.repeated.modifier", "private private record R(String foo) { }");
+        // @@@ Duplicates DatumCanNotDeclaredFieldsWithSameName
+        assertFail("compiler.err.record.cant.declare.duplicate.fields", "record R(int x, int x) {}");
+    }
+
+    public void testGoodDeclarations() {
+        assertOK("public record R() { }");
+        assertOK("record R() { }");
+        assertOK("record R() implements java.io.Serializable, Runnable { public void run() { } }");
+        assertOK("record R(int x) { }");
+        assertOK("record R(int x, int y) { }");
+        assertOK("@Deprecated record R(int x, int y) { }");
+        assertOK("record R(@Deprecated int x, int y) { }");
+        assertOK("record R<T>(T x, T y) { }");
+    }
+
+    public void testBadComponentNames() {
+        // @@@ Duplicates IllegalRecordComponentNameTest
+        for (String s : BAD_COMPONENT_NAMES)
+            assertFail("compiler.err.illegal.record.component.name", "record R(int #) { } ", s);
+    }
+
+    public void testRestrictedIdentifiers() {
+        // @@@ Duplicates BadUseOfRecordKeywordTest
+        for (String s : List.of("interface record { void m(); }",
+                "@interface record { }",
+                "class record { }",
+                "record record(int x) { }",
+                "enum record { A, B }",
+                "class R<record> { }")) {
+            assertFail("compiler.err.record.not.allowed", s);
+        }
+    }
+
+    public void testValidMembers() {
+        // @@@ Duplicates NoAddInstanceFieldsCanBeDeclaredInDatumTest (partially; rest is duplicated in testFieldDeclarations)
+        for (String s : List.of("record X(int j) { }",
+                "interface I { }",
+                "static { }",
+                "{}",
+                "enum E { A, B }",
+                "class C { }"
+        )) {
+            assertOK("record R(int i) { # }", s);
+        }
+    }
+
+    public void testCyclic() {
+        // Cyclic records are OK, but cyclic inline records would not be
+        assertOK("record R(R r) { }");
+    }
+
+    public void testBadExtends() {
+        // @@@ Duplicate RecordCantHaveExtendsTest
+        assertFail("compiler.err.expected", "record R(int x) extends Object { }");
+        assertFail("compiler.err.expected", "record R(int x) {}\n"
+                + "record R2(int x) extends R { }");
+        assertFail("compiler.err.cant.inherit.from.final", "record R(int x) {}\n"
+                + "class C extends R { }");
+    }
+
+    public void testNoExtendRecord() {
+        // @@@ Not finished, waiting on j.l.Record
+//        assertFail("",
+//                   "class R extends Record { public String toString() { return null; } public int hashCode() { return 0; } public boolean equals(Object o) { return false; } } }");
+    }
+
+    public void testFieldDeclarations() {
+        // @@@ Duplicates AllowStaticFieldsInRecordTest
+        // static fields are OK
+        assertOK("public record R(int x) {\n" +
+                "    static int I = 1;\n" +
+                "    static final String S = \"Hello World!\";\n" +
+                "    static private Object O = null;\n" +
+                "    static protected Object O2 = null;\n" +
+                "}");
+
+        // instance fields are not
+        assertFail("compiler.err.record.fields.must.be.in.header",
+                "public record R(int x) {\n" +
+                        "    private final int y = 0;" +
+                        "}");
+
+        // mutable instance fields definitely not
+        assertFail("compiler.err.record.fields.must.be.in.header",
+                "public record R(int x) {\n" +
+                        "    private int y = 0;" +
+                        "}");
+
+        // redeclaring components also not
+        assertFail("compiler.err.record.fields.must.be.in.header",
+                "public record R(int x) {\n" +
+                        "    private final int x;" +
+                        "}");
+    }
+
+    public void testAccessorRedeclaration() {
+        assertOK("public record R(int x) {\n" +
+                "    public int x() { return x; };" +
+                "}");
+
+        assertOK("public record R(int x) {\n" +
+                "    public final int x() { return 0; };" +
+                "}");
+
+        assertFail("compiler.err.method.must.be.public",
+                "public record R(int x) {\n" +
+                        "    final int x() { return 0; };" +
+                        "}");
+
+        assertFail("compiler.err.method.must.be.public",
+                "public record R(int x) {\n" +
+                        "    int x() { return 0; };" +
+                        "}");
+
+        // @@@ Error: should fail, but doesn't
+//        assertFail("something",
+//                   "public record R(int x) {\n" +
+//                   "    public int x() throws Exception { return 0; };" +
+//                   "}");
+
+        for (String s : List.of("List", "List<?>", "Object", "ArrayList<String>", "int"))
+            assertFail("compiler.err.accessor.return.type.doesnt.match",
+                    "import java.util.*;\n" +
+                            "public record R(List<String> x) {\n" +
+                            "    public # x() { return null; };" +
+                            "}", s);
+    }
+
+    public void testConstructorRedeclaration() {
+        for (String goodCtor : List.of("public R(int x) { this(x, 0); }",
+                "public R(int x, int y) { this.x = x; this.y = y; }",
+                "public R { }",
+                "public R { x = 0; }"))
+            assertOK("record R(int x, int y) { # }", goodCtor);
+
+        // Not OK to redeclare canonical without DA
+        // @@@ Should fail
+//        assertFail("", "record R(int x, int y) { # }",
+//                   "public R(int x, int y) { this.x = x; }");
+
+        // canonical ctor must be public
+        // @@@ Should fail
+//        assertFail("", "record R(int x, int y) { # }",
+//                   "R(int x, int y) { this.x = x; this.y = y; }");
+
+        // ctor args must match types
+        /*
+        assertFail("",
+                "import java.util.*;\n" +
+                        "record R(List<String> list) { # }",
+                "R(List list) { this.list = list; }");
+        */
+    }
+
+    public void testAnnotationCriteria() {
+        // OK to anno with FIELD, METHOD, PARAM, TYPE_USE, and COMPONENT, no @Target, or group thereof
+        // Not OK to anno when @Target specified and none of those are available
+        // OK to redeclare with or without same annos
+    }
+
+    public void testIllegalSerializationMembers() {
+        // readResolve, writeReplace, readObject, writeObject, readObjectNoData, serialPersistentFields
+    }
+
+    public void testLocalRecords() {
+        assertOK("class R { \n" +
+                "    void m() { \n" +
+                "        record RR(int x) { };\n" +
+                "    }\n" +
+                "}");
+
+        // Capture locals from local record
+        assertOK("class R { \n" +
+                "    void m(int y) { \n" +
+                "        record RR(int x) { public int x() { return y; }};\n" +
+                "    }\n" +
+                "}");
+    }
+}