1 /* 2 * Copyright (c) 2019, 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 8225055 27 * @summary Record types 28 * @library /tools/lib ../../lib 29 * @modules jdk.javadoc/jdk.javadoc.internal.tool 30 * @build toolbox.ToolBox javadoc.tester.* 31 * @run main TestRecordTypes 32 */ 33 34 35 import java.io.IOException; 36 import java.nio.file.Path; 37 38 import javadoc.tester.JavadocTester; 39 import toolbox.ToolBox; 40 41 public class TestRecordTypes extends JavadocTester { 42 public static void main(String... args) throws Exception { 43 TestRecordTypes tester = new TestRecordTypes(); 44 tester.runTests(m -> new Object[] { Path.of(m.getName()) }); 45 } 46 47 private final ToolBox tb = new ToolBox(); 48 49 // The following constants are set up for use with -linkoffline 50 // (but note: JDK 11 does not include java.lang.Record, so expect 51 // some 404 broken links until we can update this to a stable version.) 52 private static final String externalDocs = 53 "https://docs.oracle.com/en/java/javase/11/docs/api"; 54 private static final String localDocs = 55 Path.of(testSrc).resolve("jdk11").toUri().toString(); 56 57 @Test 58 public void testRecordKeywordUnnamedPackage(Path base) throws IOException { 59 Path src = base.resolve("src"); 60 tb.writeJavaFiles(src, 61 "public record R(int r1) { }"); 62 63 javadoc("-d", base.resolve("out").toString(), 64 "-sourcepath", src.toString(), 65 "--enable-preview", "--source", thisRelease, 66 src.resolve("R.java").toString()); 67 checkExit(Exit.OK); 68 69 checkOutput("R.html", true, 70 "<h1 title=\"Record R\" class=\"title\">Record R</h1>", 71 "public record <span class=\"typeNameLabel\">R</span>", 72 "<code><span class=\"memberNameLink\"><a href=\"#%3Cinit%3E(int)\">R</a></span>(int r1)</code>"); 73 } 74 75 @Test 76 public void testRecordKeywordNamedPackage(Path base) throws IOException { 77 Path src = base.resolve("src"); 78 tb.writeJavaFiles(src, 79 "package p; public record R(int r1) { }"); 80 81 javadoc("-d", base.resolve("out").toString(), 82 "-sourcepath", src.toString(), 83 "--enable-preview", "--source", thisRelease, 84 "p"); 85 checkExit(Exit.OK); 86 87 checkOutput("p/R.html", true, 88 "<h1 title=\"Record R\" class=\"title\">Record R</h1>", 89 "public record <span class=\"typeNameLabel\">R</span>", 90 "<code><span class=\"memberNameLink\"><a href=\"#%3Cinit%3E(int)\">R</a></span>(int r1)</code>"); 91 } 92 93 @Test 94 public void testEmptyRecord(Path base) throws IOException { 95 Path src = base.resolve("src"); 96 tb.writeJavaFiles(src, 97 "package p; public record R() { }"); 98 99 javadoc("-d", base.resolve("out").toString(), 100 "-sourcepath", src.toString(), 101 "--enable-preview", "--source", thisRelease, 102 "p"); 103 checkExit(Exit.OK); 104 105 checkOutput("p/R.html", true, 106 "<h1 title=\"Record R\" class=\"title\">Record R</h1>", 107 "public record <span class=\"typeNameLabel\">R</span>", 108 "<code><span class=\"memberNameLink\"><a href=\"#%3Cinit%3E()\">R</a></span>()</code>"); 109 } 110 111 @Test 112 public void testAtParam(Path base) throws IOException { 113 Path src = base.resolve("src"); 114 tb.writeJavaFiles(src, 115 "package p; /** This is record R. \n" 116 + " * @param r1 This is a component.\n" 117 + " */\n" 118 + "public record R(int r1) { }"); 119 120 javadoc("-d", base.resolve("out").toString(), 121 "-sourcepath", src.toString(), 122 "--enable-preview", "--source", thisRelease, 123 "p"); 124 checkExit(Exit.OK); 125 126 checkOutput("p/R.html", true, 127 "<h1 title=\"Record R\" class=\"title\">Record R</h1>", 128 "public record <span class=\"typeNameLabel\">R</span>", 129 "<dl>\n" 130 + "<dt><span class=\"paramLabel\">Record Components:</span></dt>\n" 131 + "<dd><code><a id=\"param-r1\">r1</a></code> - This is a component.</dd>\n" 132 + "</dl>", 133 "<code><span class=\"memberNameLink\"><a href=\"#%3Cinit%3E(int)\">R</a></span>(int r1)</code>"); 134 } 135 136 @Test 137 public void testAtParamTyParam(Path base) throws IOException { 138 Path src = base.resolve("src"); 139 tb.writeJavaFiles(src, 140 "package p; /** This is record R. \n" 141 + " * @param r1 This is a component.\n" 142 + " * @param <T> This is a type parameter.\n" 143 + " */\n" 144 + "public record R<T>(int r1) { }"); 145 146 javadoc("-d", base.resolve("out").toString(), 147 "-sourcepath", src.toString(), 148 "--enable-preview", "--source", thisRelease, 149 "p"); 150 checkExit(Exit.OK); 151 152 checkOutput("p/R.html", true, 153 "<h1 title=\"Record R\" class=\"title\">Record R<T></h1>", 154 "public record <span class=\"typeNameLabel\">R<T></span>", 155 "<dl>\n" 156 + "<dt><span class=\"paramLabel\">Type Parameters:</span></dt>\n" 157 + "<dd><code>T</code> - This is a type parameter.</dd>\n" 158 + "<dt><span class=\"paramLabel\">Record Components:</span></dt>\n" 159 + "<dd><code><a id=\"param-r1\">r1</a></code> - This is a component.</dd>\n" 160 + "</dl>", 161 "<code><span class=\"memberNameLink\"><a href=\"#%3Cinit%3E(int)\">R</a></span>(int r1)</code>"); 162 } 163 164 @Test 165 public void testGeneratedComments(Path base) throws IOException { 166 Path src = base.resolve("src"); 167 tb.writeJavaFiles(src, 168 "package p; /** This is record R. \n" 169 + " * @param r1 This is a component.\n" 170 + " */\n" 171 + "public record R(int r1) { }"); 172 173 javadoc("-d", base.resolve("out").toString(), 174 "-sourcepath", src.toString(), 175 "--enable-preview", "--source", thisRelease, 176 "p"); 177 checkExit(Exit.OK); 178 179 // While we don't normally test values that just come from resource files, 180 // in these cases, we want to verify that something non-empty was put into 181 // the documentation for the generated members. 182 checkOrder("p/R.html", 183 "<section class=\"constructorSummary\">", 184 "<a href=\"#%3Cinit%3E(int)\">R</a>", 185 "Creates an instance of a <code>R</code> record.", 186 "<section class=\"methodSummary\">", 187 "<a href=\"#equals(java.lang.Object)\">equals</a>", 188 "Indicates whether some other object is \"equal to\" this one.", 189 "<a href=\"#hashCode()\">hashCode</a>", 190 "Returns a hash code value for this object.", 191 "<a href=\"#r1()\">r1</a>", 192 "Returns the value of the <a href=\"#param-r1\"><code>r1</code></a> record component.", 193 "<a href=\"#toString()\">toString</a>", 194 "Returns a string representation of this record.", 195 "Method Details", 196 "<span class=\"memberName\">toString</span>", 197 "Returns a string representation of this record. The representation " 198 + "contains the name of the type, followed by the name and value of " 199 + "each of the record components.", 200 "<span class=\"memberName\">hashCode</span>", 201 "Returns a hash code value for this object. The value is derived " 202 + "from the hash code of each of the record components.", 203 "<span class=\"memberName\">equals</span>", 204 "Indicates whether some other object is \"equal to\" this one. " 205 + "The objects are equal if the other object is of the same class " 206 + "and if all the record components are equal. All components " 207 + "in this record are compared with '=='.", 208 "<span class=\"memberName\">r1</span>", 209 "Returns the value of the <a href=\"#param-r1\"><code>r1</code></a> " 210 + "record component." 211 ); 212 } 213 214 @Test 215 public void testGeneratedCommentsWithLinkOffline(Path base) throws IOException { 216 Path src = base.resolve("src"); 217 tb.writeJavaFiles(src, 218 "package p; /** This is record R. \n" 219 + " * @param r1 This is a component.\n" 220 + " */\n" 221 + "public record R(int r1) { }"); 222 223 javadoc("-d", base.resolve("out").toString(), 224 "-sourcepath", src.toString(), 225 "-linkoffline", externalDocs, localDocs, 226 "--enable-preview", "--source", thisRelease, 227 "p"); 228 checkExit(Exit.OK); 229 230 // While we don't normally test values that just come from resource files, 231 // in these cases, we want to verify that something non-empty was put into 232 // the documentation for the generated members. 233 checkOrder("p/R.html", 234 "<section class=\"constructorSummary\">", 235 "<a href=\"#%3Cinit%3E(int)\">R</a>", 236 "Creates an instance of a <code>R</code> record.", 237 "<section class=\"methodSummary\">", 238 "<a href=\"#equals(java.lang.Object)\">equals</a>", 239 "Indicates whether some other object is \"equal to\" this one.", 240 "<a href=\"#hashCode()\">hashCode</a>", 241 "Returns a hash code value for this object.", 242 "<a href=\"#r1()\">r1</a>", 243 "Returns the value of the <a href=\"#param-r1\"><code>r1</code></a> record component.", 244 "<a href=\"#toString()\">toString</a>", 245 "Returns a string representation of this record.", 246 "Method Details", 247 "<span class=\"memberName\">toString</span>", 248 "Returns a string representation of this record. The representation " 249 + "contains the name of the type, followed by the name and value of " 250 + "each of the record components.", 251 "<span class=\"memberName\">hashCode</span>", 252 "Returns a hash code value for this object. The value is derived " 253 + "from the hash code of each of the record components.", 254 "<span class=\"memberName\">equals</span>", 255 "Indicates whether some other object is \"equal to\" this one. " 256 + "The objects are equal if the other object is of the same class " 257 + "and if all the record components are equal. All components " 258 + "in this record are compared with '=='.", 259 "<span class=\"memberName\">r1</span>", 260 "Returns the value of the <a href=\"#param-r1\"><code>r1</code></a> " 261 + "record component." 262 ); 263 } 264 265 @Test 266 public void testGeneratedEqualsPrimitive(Path base) throws IOException { 267 testGeneratedEquals(base, "int a, int b", 268 "All components in this record are compared with '=='."); 269 } 270 271 @Test 272 public void testGeneratedEqualsReference(Path base) throws IOException { 273 testGeneratedEquals(base, "Object a, Object b", 274 "All components in this record are compared with <code>Objects::equals(Object,Object)</code>"); 275 } 276 277 @Test 278 public void testGeneratedEqualsMixed(Path base) throws IOException { 279 testGeneratedEquals(base, "int a, Object b", 280 "Reference components are compared with <code>Objects::equals(Object,Object)</code>; " 281 + "primitive components are compared with '=='."); 282 } 283 284 private void testGeneratedEquals(Path base, String comps, String expect) throws IOException { 285 Path src = base.resolve("src"); 286 tb.writeJavaFiles(src, 287 "package p; /** This is record R. \n" 288 + " */\n" 289 + "public record R(" + comps + ") { }"); 290 291 javadoc("-d", base.resolve("out").toString(), 292 "-sourcepath", src.toString(), 293 "--enable-preview", "--source", thisRelease, 294 "p"); 295 checkExit(Exit.OK); 296 297 checkOrder("p/R.html", expect); 298 } 299 300 @Test 301 public void testUserComments(Path base) throws IOException { 302 Path src = base.resolve("src"); 303 tb.writeJavaFiles(src, 304 "package p; /** This is record R. \n" 305 + " * @param r1 This is a component.\n" 306 + " */\n" 307 + "public record R(int r1) {\n" 308 + "/** User constructor. */ public R { }\n" 309 + "/** User equals. */ public boolean equals(Object other) { return false; }\n" 310 + "/** User hashCode. */ public int hashCode() { return 0; }\n" 311 + "/** User toString. */ public String toString() { return \"\"; }\n" 312 + "/** User accessor. */ public int r1() { return r1; }\n" 313 + "}"); 314 315 javadoc("-d", base.resolve("out").toString(), 316 "-sourcepath", src.toString(), 317 "--enable-preview", "--source", thisRelease, 318 "p"); 319 checkExit(Exit.OK); 320 321 checkOrder("p/R.html", 322 "<section class=\"constructorSummary\">", 323 "<a href=\"#%3Cinit%3E(int)\">R</a>", 324 "User constructor.", 325 "<section class=\"methodSummary\">", 326 "<a href=\"#equals(java.lang.Object)\">equals</a>", 327 "User equals.", 328 "<a href=\"#hashCode()\">hashCode</a>", 329 "User hashCode.", 330 "<a href=\"#r1()\">r1</a>", 331 "User accessor.", 332 "<a href=\"#toString()\">toString</a>", 333 "User toString." 334 ); 335 } 336 337 @Test 338 public void testExamples(Path base) throws IOException { 339 javadoc("-d", base.resolve("out-no-link").toString(), 340 "-sourcepath", testSrc.toString(), 341 "-linksource", 342 "--enable-preview", "--source", thisRelease, 343 "examples"); 344 345 checkExit(Exit.OK); 346 javadoc("-d", base.resolve("out-with-link").toString(), 347 "-sourcepath", testSrc.toString(), 348 "-linksource", 349 "-linkoffline", externalDocs, localDocs, 350 "--enable-preview", "--source", thisRelease, 351 "examples"); 352 checkExit(Exit.OK); 353 } 354 }