• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.model.psi
18 
19 import com.android.tools.lint.checks.infrastructure.TestFile
20 import com.android.tools.metalava.DriverTest
21 import com.android.tools.metalava.java
22 import org.intellij.lang.annotations.Language
23 import org.junit.Assert.assertEquals
24 import org.junit.Test
25 
26 class JavadocTest : DriverTest() {
checkStubsnull27     private fun checkStubs(
28         @Language("JAVA") source: String,
29         warnings: String? = "",
30         api: String? = null,
31         extraArguments: Array<String> = emptyArray(),
32         docStubs: Boolean = false,
33         showAnnotations: Array<String> = emptyArray(),
34         includeSourceRetentionAnnotations: Boolean = true,
35         skipEmitPackages: List<String> = listOf("java.lang", "java.util", "java.io"),
36         sourceFiles: Array<TestFile>
37     ) {
38         check(
39             sourceFiles = sourceFiles,
40             showAnnotations = showAnnotations,
41             stubFiles = arrayOf(java(source)),
42             expectedIssues = warnings,
43             checkCompilation = true,
44             api = api,
45             extraArguments = extraArguments,
46             docStubs = docStubs,
47             includeSourceRetentionAnnotations = includeSourceRetentionAnnotations,
48             skipEmitPackages = skipEmitPackages
49         )
50     }
51 
52     @Test
Test package to package infonull53     fun `Test package to package info`() {
54         @Language("HTML")
55         val html = """
56             <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
57             <!-- not a body tag: <body> -->
58             <html>
59             <body bgcolor="white">
60             My package docs<br>
61             <!-- comment -->
62             Sample code: /** code here */
63             Another line.<br>
64             </BODY>
65             </html>
66             """
67 
68         @Suppress("DanglingJavadoc")
69         @Language("JAVA")
70         val java = """
71             /**
72              * My package docs<br>
73              * <!-- comment -->
74              * Sample code: /** code here &#42;/
75              * Another line.<br>
76              */
77             """
78 
79         assertEquals(java.trimIndent() + "\n", packageHtmlToJavadoc(html.trimIndent()))
80     }
81 
82     @Test
Relative documentation links in stubsnull83     fun `Relative documentation links in stubs`() {
84         checkStubs(
85             docStubs = false,
86             sourceFiles = arrayOf(
87                 java(
88                     """
89                     package test.pkg1;
90                     import java.io.IOException;
91                     import test.pkg2.OtherClass;
92 
93                     /**
94                      *  Blah blah {@link OtherClass} blah blah.
95                      *  Referencing <b>field</b> {@link OtherClass#foo},
96                      *  and referencing method {@link OtherClass#bar(int,
97                      *   boolean)}.
98                      *  And relative method reference {@link #baz()}.
99                      *  And relative field reference {@link #importance}.
100                      *  Here's an already fully qualified reference: {@link test.pkg2.OtherClass}.
101                      *  And here's one in the same package: {@link LocalClass}.
102                      *
103                      *  @deprecated For some reason
104                      *  @see OtherClass
105                      *  @see OtherClass#bar(int, boolean)
106                      */
107                     @SuppressWarnings("all")
108                     public class SomeClass {
109                        /**
110                        * My method.
111                        * @param focus The focus to find. One of {@link OtherClass#FOCUS_INPUT} or
112                        *         {@link OtherClass#FOCUS_ACCESSIBILITY}.
113                        * @throws IOException when blah blah blah
114                        * @throws {@link RuntimeException} when blah blah blah
115                        */
116                        public void baz(int focus) throws IOException;
117                        public boolean importance;
118                     }
119                     """
120                 ),
121                 java(
122                     """
123                     package test.pkg2;
124 
125                     @SuppressWarnings("all")
126                     public class OtherClass {
127                         public static final int FOCUS_INPUT = 1;
128                         public static final int FOCUS_ACCESSIBILITY = 2;
129                         public int foo;
130                         public void bar(int baz, boolean bar);
131                     }
132                     """
133                 ),
134                 java(
135                     """
136                     package test.pkg1;
137 
138                     @SuppressWarnings("all")
139                     public class LocalClass {
140                     }
141                     """
142                 )
143             ),
144             warnings = "",
145             source = """
146                     package test.pkg1;
147                     import test.pkg2.OtherClass;
148                     import java.io.IOException;
149                     /**
150                      *  Blah blah {@link test.pkg2.OtherClass OtherClass} blah blah.
151                      *  Referencing <b>field</b> {@link test.pkg2.OtherClass#foo OtherClass#foo},
152                      *  and referencing method {@link test.pkg2.OtherClass#bar(int,boolean) OtherClass#bar(int,
153                      *   boolean)}.
154                      *  And relative method reference {@link #baz()}.
155                      *  And relative field reference {@link #importance}.
156                      *  Here's an already fully qualified reference: {@link test.pkg2.OtherClass}.
157                      *  And here's one in the same package: {@link test.pkg1.LocalClass LocalClass}.
158                      *
159                      *  @deprecated For some reason
160                      *  @see test.pkg2.OtherClass
161                      *  @see test.pkg2.OtherClass#bar(int, boolean)
162                      */
163                     @SuppressWarnings({"unchecked", "deprecation", "all"})
164                     @Deprecated
165                     public class SomeClass {
166                     @Deprecated
167                     public SomeClass() { throw new RuntimeException("Stub!"); }
168                     /**
169                      * My method.
170                      * @param focus The focus to find. One of {@link test.pkg2.OtherClass#FOCUS_INPUT OtherClass#FOCUS_INPUT} or
171                      *         {@link test.pkg2.OtherClass#FOCUS_ACCESSIBILITY OtherClass#FOCUS_ACCESSIBILITY}.
172                      * @throws java.io.IOException when blah blah blah
173                      * @throws {@link java.lang.RuntimeException RuntimeException} when blah blah blah
174                      */
175                     @Deprecated
176                     public void baz(int focus) throws java.io.IOException { throw new RuntimeException("Stub!"); }
177                     @Deprecated public boolean importance;
178                     }
179                     """
180         )
181     }
182 
183     @Test
Rewrite relative documentation links in doc-stubsnull184     fun `Rewrite relative documentation links in doc-stubs`() {
185         checkStubs(
186             docStubs = true,
187             sourceFiles = arrayOf(
188                 java(
189                     """
190                     package test.pkg1;
191                     import java.io.IOException;
192                     import test.pkg2.OtherClass;
193 
194                     /**
195                      *  Blah blah {@link OtherClass} blah blah.
196                      *  Referencing <b>field</b> {@link OtherClass#foo},
197                      *  and referencing method {@link OtherClass#bar(int,
198                      *   boolean)}.
199                      *  And relative method reference {@link #baz()}.
200                      *  And relative field reference {@link #importance}.
201                      *  Here's an already fully qualified reference: {@link test.pkg2.OtherClass}.
202                      *  And here's one in the same package: {@link LocalClass}.
203                      *
204                      *  @deprecated For some reason
205                      *  @see OtherClass
206                      *  @see OtherClass#bar(int, boolean)
207                      */
208                     @SuppressWarnings("all")
209                     public class SomeClass {
210                        /**
211                        * My method.
212                        * @param focus The focus to find. One of {@link OtherClass#FOCUS_INPUT} or
213                        *         {@link OtherClass#FOCUS_ACCESSIBILITY}.
214                        * @throws java.io.IOException when blah blah blah
215                        * @throws {@link java.lang.RuntimeException} when blah blah blah
216                        */
217                        public void baz(int focus) throws IOException;
218                        public boolean importance;
219                     }
220                     """
221                 ),
222                 java(
223                     """
224                     package test.pkg2;
225 
226                     @SuppressWarnings("all")
227                     public class OtherClass {
228                         public static final int FOCUS_INPUT = 1;
229                         public static final int FOCUS_ACCESSIBILITY = 2;
230                         public int foo;
231                         public void bar(int baz, boolean bar);
232                     }
233                     """
234                 ),
235                 java(
236                     """
237                     package test.pkg1;
238 
239                     @SuppressWarnings("all")
240                     public class LocalClass {
241                     }
242                     """
243                 )
244             ),
245             warnings = "",
246             source = """
247                 package test.pkg1;
248                 import test.pkg2.OtherClass;
249                 import java.io.IOException;
250                 /**
251                  *  Blah blah {@link test.pkg2.OtherClass OtherClass} blah blah.
252                  *  Referencing <b>field</b> {@link test.pkg2.OtherClass#foo OtherClass#foo},
253                  *  and referencing method {@link test.pkg2.OtherClass#bar(int,boolean) OtherClass#bar(int,
254                  *   boolean)}.
255                  *  And relative method reference {@link #baz()}.
256                  *  And relative field reference {@link #importance}.
257                  *  Here's an already fully qualified reference: {@link test.pkg2.OtherClass}.
258                  *  And here's one in the same package: {@link test.pkg1.LocalClass LocalClass}.
259                  *
260                  *  @deprecated For some reason
261                  *  @see test.pkg2.OtherClass
262                  *  @see test.pkg2.OtherClass#bar(int, boolean)
263                  */
264                 @SuppressWarnings({"unchecked", "deprecation", "all"})
265                 @Deprecated
266                 public class SomeClass {
267                 @Deprecated
268                 public SomeClass() { throw new RuntimeException("Stub!"); }
269                 /**
270                  * My method.
271                  * @param focus The focus to find. One of {@link test.pkg2.OtherClass#FOCUS_INPUT OtherClass#FOCUS_INPUT} or
272                  *         {@link test.pkg2.OtherClass#FOCUS_ACCESSIBILITY OtherClass#FOCUS_ACCESSIBILITY}.
273                  * @throws java.io.IOException when blah blah blah
274                  * @throws {@link java.lang.RuntimeException} when blah blah blah
275                  */
276                 @Deprecated
277                 public void baz(int focus) throws java.io.IOException { throw new RuntimeException("Stub!"); }
278                 @Deprecated public boolean importance;
279                 }
280                 """
281         )
282     }
283 
284     @Test
Rewrite relative documentation links in doc-stubs 2null285     fun `Rewrite relative documentation links in doc-stubs 2`() {
286         // Properly handle links to inherited methods
287         checkStubs(
288             docStubs = true,
289             sourceFiles = arrayOf(
290                 java(
291                     """
292                     package test.pkg1;
293                     import java.io.IOException;
294 
295                     @SuppressWarnings("all")
296                     public class R {
297                         public static class attr {
298                             /**
299                              * Resource identifier to assign to this piece of named meta-data.
300                              * The resource identifier can later be retrieved from the meta data
301                              * Bundle through {@link android.os.Bundle#getInt Bundle.getInt}.
302                              * <p>May be a reference to another resource, in the form
303                              * "<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>" or a theme
304                              * attribute in the form
305                              * "<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>".
306                              */
307                             public static final int resource=0x01010025;
308                         }
309                     }
310                     """
311                 ),
312                 java(
313                     """
314                     package android.os;
315 
316                     @SuppressWarnings("all")
317                     public class Bundle extends BaseBundle {
318                     }
319                     """
320                 ),
321                 java(
322                     """
323                     package android.os;
324 
325                     @SuppressWarnings("all")
326                     public class BaseBundle {
327                         public int getInt(String key) {
328                             return getInt(key, 0);
329                         }
330 
331                         public int getInt(String key, int defaultValue) {
332                             return defaultValue;
333                         }
334                     }
335                     """
336                 )
337             ),
338             warnings = "",
339             source = """
340                 package test.pkg1;
341                 @SuppressWarnings({"unchecked", "deprecation", "all"})
342                 public class R {
343                 public R() { throw new RuntimeException("Stub!"); }
344                 @SuppressWarnings({"unchecked", "deprecation", "all"})
345                 public static class attr {
346                 public attr() { throw new RuntimeException("Stub!"); }
347                 /**
348                  * Resource identifier to assign to this piece of named meta-data.
349                  * The resource identifier can later be retrieved from the meta data
350                  * Bundle through {@link android.os.Bundle#getInt Bundle.getInt}.
351                  * <p>May be a reference to another resource, in the form
352                  * "<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>" or a theme
353                  * attribute in the form
354                  * "<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>".
355                  */
356                 public static final int resource = 16842789; // 0x1010025
357                 }
358                 }
359                 """
360         )
361     }
362 
363     @Test
Rewrite relative documentation links in doc-stubs 3null364     fun `Rewrite relative documentation links in doc-stubs 3`() {
365         checkStubs(
366             docStubs = true,
367             sourceFiles = arrayOf(
368                 java(
369                     """
370                     package android.accessibilityservice;
371 
372                     import android.view.accessibility.AccessibilityEvent;
373                     import android.view.accessibility.AccessibilityRecord;
374 
375                     /**
376                      * <p>
377                      * Window content may be retrieved with
378                      * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()}.
379                      * Mention AccessibilityRecords here.
380                      * </p>
381                      */
382                     @SuppressWarnings("all")
383                     public abstract class AccessibilityService {
384                     }
385                     """
386                 ),
387                 java(
388                     """
389                     package android.view.accessibility;
390 
391                     @SuppressWarnings("all")
392                     public final class AccessibilityEvent extends AccessibilityRecord {
393                     }
394                     """
395                 ),
396                 java(
397                     """
398                     package android.view.accessibility;
399 
400                     @SuppressWarnings("all")
401                     public class AccessibilityRecord {
402                         public AccessibilityNodeInfo getSource() {
403                             return null;
404                         }
405                     }
406                     """
407                 ),
408                 java(
409                     """
410                     package android.view.accessibility;
411                     public class AccessibilityNodeInfo {}
412                     """
413                 )
414             ),
415             warnings = "",
416             source = """
417                 package android.accessibilityservice;
418                 import android.view.accessibility.AccessibilityEvent;
419                 /**
420                  * <p>
421                  * Window content may be retrieved with
422                  * {@link android.view.accessibility.AccessibilityEvent#getSource() AccessibilityEvent.getSource()}.
423                  * Mention AccessibilityRecords here.
424                  * </p>
425                  */
426                 @SuppressWarnings({"unchecked", "deprecation", "all"})
427                 public abstract class AccessibilityService {
428                 public AccessibilityService() { throw new RuntimeException("Stub!"); }
429                 }
430                 """
431         )
432     }
433 
434     @Test
Rewrite relative documentation links in doc-stubs but preserve custom link textnull435     fun `Rewrite relative documentation links in doc-stubs but preserve custom link text`() {
436         checkStubs(
437             docStubs = true,
438             sourceFiles = arrayOf(
439                 java(
440                     """
441                     package android.accessibilityservice;
442 
443                     import android.view.accessibility.AccessibilityEvent;
444                     import android.view.accessibility.AccessibilityRecord;
445 
446                     /**
447                      * <p>
448                      * Window content may be retrieved with
449                      * {@link AccessibilityEvent#getSource() this_method}.
450                      * Mention AccessibilityRecords here.
451                      * </p>
452                      */
453                     @SuppressWarnings("all")
454                     public abstract class AccessibilityService {
455                     }
456                     """
457                 ),
458                 java(
459                     """
460                     package android.view.accessibility;
461 
462                     @SuppressWarnings("all")
463                     public final class AccessibilityEvent extends AccessibilityRecord {
464                     }
465                     """
466                 ),
467                 java(
468                     """
469                     package android.view.accessibility;
470 
471                     @SuppressWarnings("all")
472                     public class AccessibilityRecord {
473                         public AccessibilityNodeInfo getSource() {
474                             return null;
475                         }
476                     }
477                     """
478                 ),
479                 java(
480                     """
481                     package android.view.accessibility;
482                     public class AccessibilityNodeInfo {}
483                     """
484                 )
485             ),
486             warnings = "",
487             source = """
488                 package android.accessibilityservice;
489                 import android.view.accessibility.AccessibilityEvent;
490                 /**
491                  * <p>
492                  * Window content may be retrieved with
493                  * {@link android.view.accessibility.AccessibilityEvent#getSource() this_method}.
494                  * Mention AccessibilityRecords here.
495                  * </p>
496                  */
497                 @SuppressWarnings({"unchecked", "deprecation", "all"})
498                 public abstract class AccessibilityService {
499                 public AccessibilityService() { throw new RuntimeException("Stub!"); }
500                 }
501                 """
502         )
503     }
504 
505     @Test
Rewrite relative documentation links in doc-stubs 4null506     fun `Rewrite relative documentation links in doc-stubs 4`() {
507         checkStubs(
508             docStubs = true,
509             sourceFiles = arrayOf(
510                 java(
511                     """
512                     package android.content;
513 
514                     import android.os.OperationCanceledException;
515 
516                     @SuppressWarnings("all")
517                     public abstract class AsyncTaskLoader<D> {
518                         /**
519                          * Called if the task was canceled before it was completed.  Gives the class a chance
520                          * to clean up post-cancellation and to properly dispose of the result.
521                          *
522                          * @param data The value that was returned by {@link #loadInBackground}, or null
523                          * if the task threw {@link OperationCanceledException}.
524                          */
525                         public void onCanceled(D data) {
526                         }
527 
528                         /**
529                          * Called on a worker thread to perform the actual load and to return
530                          * the result of the load operation.
531                          *
532                          * Implementations should not deliver the result directly, but should return them
533                          * from this method, which will eventually end up calling {@link #deliverResult} on
534                          * the UI thread.  If implementations need to process the results on the UI thread
535                          * they may override {@link #deliverResult} and do so there.
536                          *
537                          * When the load is canceled, this method may either return normally or throw
538                          * {@link OperationCanceledException}.  In either case, the Loader will
539                          * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
540                          * result object, if any.
541                          *
542                          * @return The result of the load operation.
543                          *
544                          * @throws OperationCanceledException if the load is canceled during execution.
545                          *
546                          * @see #onCanceled
547                          */
548                         public abstract Object loadInBackground();
549 
550                         /**
551                          * Sends the result of the load to the registered listener. Should only be called by subclasses.
552                          *
553                          * Must be called from the process's main thread.
554                          *
555                          * @param data the result of the load
556                          */
557                         public void deliverResult(Object data) {
558                         }
559                     }
560                     """
561                 ),
562                 java(
563                     """
564                     package android.os;
565 
566 
567                     /**
568                      * An exception type that is thrown when an operation in progress is canceled.
569                      */
570                     @SuppressWarnings("all")
571                     public class OperationCanceledException extends RuntimeException {
572                         public OperationCanceledException() {
573                             this(null);
574                         }
575 
576                         public OperationCanceledException(String message) {
577                             super(message != null ? message : "The operation has been canceled.");
578                         }
579                     }
580                     """
581                 )
582             ),
583             warnings = "",
584             source = """
585                 package android.content;
586                 import android.os.OperationCanceledException;
587                 @SuppressWarnings({"unchecked", "deprecation", "all"})
588                 public abstract class AsyncTaskLoader<D> {
589                 public AsyncTaskLoader() { throw new RuntimeException("Stub!"); }
590                 /**
591                  * Called if the task was canceled before it was completed.  Gives the class a chance
592                  * to clean up post-cancellation and to properly dispose of the result.
593                  *
594                  * @param data The value that was returned by {@link #loadInBackground}, or null
595                  * if the task threw {@link android.os.OperationCanceledException OperationCanceledException}.
596                  */
597                 public void onCanceled(D data) { throw new RuntimeException("Stub!"); }
598                 /**
599                  * Called on a worker thread to perform the actual load and to return
600                  * the result of the load operation.
601                  *
602                  * Implementations should not deliver the result directly, but should return them
603                  * from this method, which will eventually end up calling {@link #deliverResult} on
604                  * the UI thread.  If implementations need to process the results on the UI thread
605                  * they may override {@link #deliverResult} and do so there.
606                  *
607                  * When the load is canceled, this method may either return normally or throw
608                  * {@link android.os.OperationCanceledException OperationCanceledException}.  In either case, the Loader will
609                  * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
610                  * result object, if any.
611                  *
612                  * @return The result of the load operation.
613                  *
614                  * @throws android.os.OperationCanceledException if the load is canceled during execution.
615                  *
616                  * @see #onCanceled
617                  */
618                 public abstract java.lang.Object loadInBackground();
619                 /**
620                  * Sends the result of the load to the registered listener. Should only be called by subclasses.
621                  *
622                  * Must be called from the process's main thread.
623                  *
624                  * @param data the result of the load
625                  */
626                 public void deliverResult(java.lang.Object data) { throw new RuntimeException("Stub!"); }
627                 }
628                 """
629         )
630     }
631 
632     @Test
Rewrite relative documentation links in doc-stubs 5null633     fun `Rewrite relative documentation links in doc-stubs 5`() {
634         // Properly handle links to inherited methods
635         checkStubs(
636             docStubs = true,
637             sourceFiles = arrayOf(
638                 java(
639                     """
640                     package org.xmlpull.v1;
641 
642                     /**
643                      * Example docs.
644                      * <pre>
645                      * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException</a>;
646                      *         xpp.<a href="#setInput">setInput</a>( new StringReader ( "&lt;foo>Hello World!&lt;/foo>" ) );
647                      * </pre>
648                      * see #setInput
649                      */
650                     @SuppressWarnings("all")
651                     public interface XmlPullParser {
652                         void setInput();
653                     }
654                     """
655                 )
656             ),
657             warnings = "",
658             source = """
659                 package org.xmlpull.v1;
660                 /**
661                  * Example docs.
662                  * <pre>
663                  * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException</a>;
664                  *         xpp.<a href="#setInput">setInput</a>( new StringReader ( "&lt;foo>Hello World!&lt;/foo>" ) );
665                  * </pre>
666                  * see #setInput
667                  */
668                 @SuppressWarnings({"unchecked", "deprecation", "all"})
669                 public interface XmlPullParser {
670                 public void setInput();
671                 }
672                 """
673         )
674     }
675 
676     @Test
Check references to inherited field constantsnull677     fun `Check references to inherited field constants`() {
678         checkStubs(
679             docStubs = true,
680             warnings = "",
681             sourceFiles = arrayOf(
682                 java(
683                     """
684                     package test.pkg1;
685                     import test.pkg2.MyChild;
686 
687                     /**
688                      * Reference to {@link MyChild#CONSTANT1},
689                      * {@link MyChild#CONSTANT2}, and
690                      * {@link MyChild#myMethod}.
691                      * <p>
692                      * Absolute reference:
693                      * {@link test.pkg2.MyChild#CONSTANT1 MyChild.CONSTANT1}
694                      * <p>
695                      * Inner class reference:
696                      * {@link Test.TestInner#CONSTANT3}, again
697                      * {@link TestInner#CONSTANT3}
698                      *
699                      * @see test.pkg2.MyChild#myMethod
700                      */
701                     @SuppressWarnings("all")
702                     public class Test {
703                         public static class TestInner {
704                             public static final String CONSTANT3 = "Hello";
705                         }
706                     }
707                     """
708                 ),
709                 java(
710                     """
711                     package test.pkg1;
712                     @SuppressWarnings("all")
713                     interface MyConstants {
714                         long CONSTANT1 = 12345;
715                     }
716                     """
717                 ),
718                 java(
719                     """
720                     package test.pkg1;
721                     import java.io.Closeable;
722                     @SuppressWarnings("all")
723                     class MyParent implements MyConstants, Closeable {
724                         public static final long CONSTANT2 = 67890;
725                         public void myMethod() {
726                         }
727                     }
728                     """
729                 ),
730                 java(
731                     """
732                     package test.pkg2;
733 
734                     import test.pkg1.MyParent;
735                     @SuppressWarnings("all")
736                     public class MyChild extends MyParent implements MyConstants {
737                         @Override
738                         public void close() {}
739                     }
740                     """
741                 )
742             ),
743             source = """
744                 package test.pkg1;
745                 import test.pkg2.MyChild;
746                 /**
747                  * Reference to {@link test.pkg2.MyChild#CONSTANT1 MyChild#CONSTANT1},
748                  * {@link test.pkg2.MyChild#CONSTANT2 MyChild#CONSTANT2}, and
749                  * {@link test.pkg2.MyChild#myMethod MyChild#myMethod}.
750                  * <p>
751                  * Absolute reference:
752                  * {@link test.pkg2.MyChild#CONSTANT1 MyChild.CONSTANT1}
753                  * <p>
754                  * Inner class reference:
755                  * {@link test.pkg1.Test.TestInner#CONSTANT3 Test.TestInner#CONSTANT3}, again
756                  * {@link test.pkg1.Test.TestInner#CONSTANT3 TestInner#CONSTANT3}
757                  *
758                  * @see test.pkg2.MyChild#myMethod
759                  */
760                 @SuppressWarnings({"unchecked", "deprecation", "all"})
761                 public class Test {
762                 public Test() { throw new RuntimeException("Stub!"); }
763                 @SuppressWarnings({"unchecked", "deprecation", "all"})
764                 public static class TestInner {
765                 public TestInner() { throw new RuntimeException("Stub!"); }
766                 public static final java.lang.String CONSTANT3 = "Hello";
767                 }
768                 }
769                 """
770         )
771     }
772 
773     @Test
Handle @attr referencesnull774     fun `Handle @attr references`() {
775         checkStubs(
776             docStubs = true,
777             warnings = "",
778             sourceFiles = arrayOf(
779                 java(
780                     """
781                     package test.pkg1;
782 
783                     @SuppressWarnings("all")
784                     public class Test {
785                         /**
786                          * Returns the drawable that will be drawn between each item in the list.
787                          *
788                          * @return the current drawable drawn between list elements
789                          * This value may be {@code null}.
790                          * @attr ref R.styleable#ListView_divider
791                          */
792                         public Object getFoo() {
793                             return null;
794                         }
795                     }
796                     """
797                 )
798             ),
799             source = """
800                 package test.pkg1;
801                 @SuppressWarnings({"unchecked", "deprecation", "all"})
802                 public class Test {
803                 public Test() { throw new RuntimeException("Stub!"); }
804                 /**
805                  * Returns the drawable that will be drawn between each item in the list.
806                  *
807                  * @return the current drawable drawn between list elements
808                  * This value may be {@code null}.
809                  * @attr ref android.R.styleable#ListView_divider
810                  */
811                 public java.lang.Object getFoo() { throw new RuntimeException("Stub!"); }
812                 }
813                 """
814         )
815     }
816 
817     @Test
Rewrite parameter listnull818     fun `Rewrite parameter list`() {
819         checkStubs(
820             docStubs = true,
821             warnings = "",
822             sourceFiles = arrayOf(
823                 java(
824                     """
825                     package test.pkg1;
826                     import test.pkg2.OtherClass1;
827                     import test.pkg2.OtherClass2;
828 
829                     /**
830                      * Reference to {@link OtherClass1#myMethod(OtherClass2, int name, OtherClass2[])},
831                      */
832                     @SuppressWarnings("all")
833                     public class Test<E extends OtherClass2> {
834                         /**
835                          * Reference to {@link OtherClass1#myMethod(E, int, OtherClass2 [])},
836                          */
837                         public void test() { }
838                     }
839                     """
840                 ),
841                 java(
842                     """
843                     package test.pkg2;
844 
845                     @SuppressWarnings("all")
846                     class OtherClass1 {
847                         public void myMethod(OtherClass2 parameter1, int parameter2, OtherClass2[] parameter3) {
848                         }
849                     }
850                     """
851                 ),
852                 java(
853                     """
854                     package test.pkg2;
855 
856                     @SuppressWarnings("all")
857                     public class OtherClass2 {
858                     }
859                     """
860                 )
861             ),
862             source = """
863                 package test.pkg1;
864                 import test.pkg2.OtherClass2;
865                 /**
866                  * Reference to {@link test.pkg2.OtherClass1#myMethod(test.pkg2.OtherClass2,int name,test.pkg2.OtherClass2[]) OtherClass1#myMethod(OtherClass2, int name, OtherClass2[])},
867                  */
868                 @SuppressWarnings({"unchecked", "deprecation", "all"})
869                 public class Test<E extends test.pkg2.OtherClass2> {
870                 public Test() { throw new RuntimeException("Stub!"); }
871                 /**
872                  * Reference to {@link test.pkg2.OtherClass1#myMethod(E,int,test.pkg2.OtherClass2[]) OtherClass1#myMethod(E, int, OtherClass2 [])},
873                  */
874                 public void test() { throw new RuntimeException("Stub!"); }
875                 }
876                 """
877         )
878     }
879 
880     @Test
Rewrite parameter list 2null881     fun `Rewrite parameter list 2`() {
882         checkStubs(
883             docStubs = true,
884             warnings = "",
885             sourceFiles = arrayOf(
886                 java(
887                     """
888                     package test.pkg1;
889                     import java.nio.ByteBuffer;
890 
891                     @SuppressWarnings("all")
892                     public abstract class Test {
893                         /**
894                          * Blah blah
895                          * <blockquote><pre>
896                          * {@link #wrap(ByteBuffer [], int, int, ByteBuffer)
897                          *     engine.wrap(new ByteBuffer [] { src }, 0, 1, dst);}
898                          * </pre></blockquote>
899                          */
900                         public void test() { }
901 
902                         public abstract void wrap(ByteBuffer [] srcs, int offset,
903                             int length, ByteBuffer dst);
904                     }
905                     """
906                 )
907             ),
908             source = """
909                 package test.pkg1;
910                 import java.nio.ByteBuffer;
911                 @SuppressWarnings({"unchecked", "deprecation", "all"})
912                 public abstract class Test {
913                 public Test() { throw new RuntimeException("Stub!"); }
914                 /**
915                  * Blah blah
916                  * <blockquote><pre>
917                  * {@link #wrap(java.nio.ByteBuffer[],int,int,java.nio.ByteBuffer)
918                  *     engine.wrap(new ByteBuffer [] { src }, 0, 1, dst);}
919                  * </pre></blockquote>
920                  */
921                 public void test() { throw new RuntimeException("Stub!"); }
922                 public abstract void wrap(java.nio.ByteBuffer[] srcs, int offset, int length, java.nio.ByteBuffer dst);
923                 }
924                 """
925         )
926     }
927 
928     @Test
Warn about unresolvednull929     fun `Warn about unresolved`() {
930         @Suppress("ConstantConditionIf")
931         checkStubs(
932             docStubs = true,
933             warnings =
934             if (REPORT_UNRESOLVED_SYMBOLS) {
935                 """
936                 src/test/pkg1/Test.java:6: lint: Unresolved documentation reference: SomethingMissing [UnresolvedLink]
937                 src/test/pkg1/Test.java:6: lint: Unresolved documentation reference: OtherMissing [UnresolvedLink]
938             """
939             } else {
940                 ""
941             },
942             sourceFiles = arrayOf(
943                 java(
944                     """
945                     package test.pkg1;
946                     import java.nio.ByteBuffer;
947 
948                     @SuppressWarnings("all")
949                     public class Test {
950                         /**
951                          * Reference to {@link SomethingMissing} and
952                          * {@link String#randomMethod}.
953                          *
954                          * @see OtherMissing
955                          */
956                         public void test() { }
957                     }
958                     """
959                 )
960             ),
961             source = """
962                 package test.pkg1;
963                 @SuppressWarnings({"unchecked", "deprecation", "all"})
964                 public class Test {
965                 public Test() { throw new RuntimeException("Stub!"); }
966                 /**
967                  * Reference to {@link SomethingMissing} and
968                  * {@link java.lang.String#randomMethod String#randomMethod}.
969                  *
970                  * @see OtherMissing
971                  */
972                 public void test() { throw new RuntimeException("Stub!"); }
973                 }
974                 """
975         )
976     }
977 
978     @Test
Javadoc link to innerclass constructornull979     fun `Javadoc link to innerclass constructor`() {
980         check(
981             sourceFiles = arrayOf(
982                 java(
983                     """
984                     package android.view;
985                     import android.graphics.Insets;
986 
987                     public final class WindowInsets {
988                         /**
989                          * Returns a copy of this WindowInsets with selected system window insets replaced
990                          * with new values.
991                          *
992                          * @param left New left inset in pixels
993                          * @param top New top inset in pixels
994                          * @param right New right inset in pixels
995                          * @param bottom New bottom inset in pixels
996                          * @return A modified copy of this WindowInsets
997                          * @deprecated use {@link Builder#Builder(WindowInsets)} with
998                          *             {@link Builder#setSystemWindowInsets(Insets)} instead.
999                          */
1000                         @Deprecated
1001                         public WindowInsets replaceSystemWindowInsets(int left, int top, int right, int bottom) {
1002 
1003                         }
1004 
1005                         public static class Builder {
1006                             public Builder() {
1007                             }
1008 
1009                             public Builder(WindowInsets insets) {
1010                             }
1011 
1012                             public Builder setSystemWindowInsets(Insets systemWindowInsets) {
1013                                 return this;
1014                             }
1015                         }
1016                     }
1017                     """
1018                 ),
1019                 java(
1020                     """
1021                     package android.graphics;
1022                     public class Insets {
1023                     }
1024                     """
1025                 )
1026             ),
1027             docStubs = true,
1028             stubFiles = arrayOf(
1029                 java(
1030                     """
1031                     package android.view;
1032                     import android.graphics.Insets;
1033                     @SuppressWarnings({"unchecked", "deprecation", "all"})
1034                     public final class WindowInsets {
1035                     public WindowInsets() { throw new RuntimeException("Stub!"); }
1036                     /**
1037                      * Returns a copy of this WindowInsets with selected system window insets replaced
1038                      * with new values.
1039                      *
1040                      * @param left New left inset in pixels
1041                      * @param top New top inset in pixels
1042                      * @param right New right inset in pixels
1043                      * @param bottom New bottom inset in pixels
1044                      * @return A modified copy of this WindowInsets
1045                      * @deprecated use {@link android.view.WindowInsets.Builder#Builder(android.view.WindowInsets) Builder#Builder(WindowInsets)} with
1046                      *             {@link android.view.WindowInsets.Builder#setSystemWindowInsets(android.graphics.Insets) Builder#setSystemWindowInsets(Insets)} instead.
1047                      */
1048                     @Deprecated
1049                     public android.view.WindowInsets replaceSystemWindowInsets(int left, int top, int right, int bottom) { throw new RuntimeException("Stub!"); }
1050                     @SuppressWarnings({"unchecked", "deprecation", "all"})
1051                     public static class Builder {
1052                     public Builder() { throw new RuntimeException("Stub!"); }
1053                     public Builder(android.view.WindowInsets insets) { throw new RuntimeException("Stub!"); }
1054                     public android.view.WindowInsets.Builder setSystemWindowInsets(android.graphics.Insets systemWindowInsets) { throw new RuntimeException("Stub!"); }
1055                     }
1056                     }
1057                     """
1058                 )
1059             )
1060         )
1061     }
1062 
1063     @Test
Ensure references to classes in JavaDoc of hidden members do not affect importsnull1064     fun `Ensure references to classes in JavaDoc of hidden members do not affect imports`() {
1065         check(
1066             sourceFiles = arrayOf(
1067                 java(
1068                     """
1069                     package test.pkg;
1070                     import test.pkg.bar.Bar;
1071                     import test.pkg.baz.Baz;
1072                     public class Foo {
1073                         /**
1074                          * This method is hidden so the reference to {@link Baz} in this comment
1075                          * should not cause test.pkg.baz.Baz import to be added even though Baz is
1076                          * part of the API.
1077                          * @hide
1078                          */
1079                         public void baz() {}
1080 
1081                         /**
1082                          * @see Bar
1083                          */
1084                         public void bar() {}
1085                     }
1086                     """
1087                 ),
1088                 java(
1089                     """
1090                     package test.pkg.bar;
1091                     import test.pkg.Foo;
1092                     import test.pkg.baz.Baz;
1093                     public class Bar {
1094                         /** @see Baz */
1095                         public void baz(Baz baz) {}
1096                         /** @see Foo */
1097                         public void foo(Foo foo) {}
1098                     }
1099                     """
1100                 ),
1101                 java(
1102                     """
1103                     package test.pkg.baz;
1104                     public class Baz {
1105                     }
1106                     """
1107                 )
1108             ),
1109             stubFiles = arrayOf(
1110                 java(
1111                     """
1112                     package test.pkg;
1113                     import test.pkg.bar.Bar;
1114                     @SuppressWarnings({"unchecked", "deprecation", "all"})
1115                     public class Foo {
1116                     public Foo() { throw new RuntimeException("Stub!"); }
1117                     /**
1118                      * @see test.pkg.bar.Bar
1119                      */
1120                     public void bar() { throw new RuntimeException("Stub!"); }
1121                     }
1122                     """
1123                 ),
1124                 java(
1125                     """
1126                     package test.pkg.bar;
1127                     import test.pkg.baz.Baz;
1128                     import test.pkg.Foo;
1129                     @SuppressWarnings({"unchecked", "deprecation", "all"})
1130                     public class Bar {
1131                     public Bar() { throw new RuntimeException("Stub!"); }
1132                     /** @see test.pkg.baz.Baz */
1133                     public void baz(test.pkg.baz.Baz baz) { throw new RuntimeException("Stub!"); }
1134                     /** @see test.pkg.Foo */
1135                     public void foo(test.pkg.Foo foo) { throw new RuntimeException("Stub!"); }
1136                     }
1137                     """
1138                 ),
1139                 java(
1140                     """
1141                     package test.pkg.baz;
1142                     @SuppressWarnings({"unchecked", "deprecation", "all"})
1143                     public class Baz {
1144                     public Baz() { throw new RuntimeException("Stub!"); }
1145                     }
1146                     """
1147                 )
1148             )
1149         )
1150     }
1151 }
1152