| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tools.metalava.model.psi |
| |
| import com.android.tools.lint.checks.infrastructure.TestFile |
| import com.android.tools.metalava.DriverTest |
| import org.intellij.lang.annotations.Language |
| import org.junit.Assert.assertEquals |
| import org.junit.Test |
| |
| class JavadocTest : DriverTest() { |
| private fun checkStubs( |
| @Language("JAVA") source: String, |
| compatibilityMode: Boolean = true, |
| warnings: String? = "", |
| api: String? = null, |
| extraArguments: Array<String> = emptyArray(), |
| docStubs: Boolean = false, |
| showAnnotations: Array<String> = emptyArray(), |
| includeSourceRetentionAnnotations: Boolean = true, |
| skipEmitPackages: List<String> = listOf("java.lang", "java.util", "java.io"), |
| sourceFiles: Array<TestFile> |
| ) { |
| check( |
| sourceFiles = sourceFiles, |
| showAnnotations = showAnnotations, |
| stubs = arrayOf(source), |
| compatibilityMode = compatibilityMode, |
| expectedIssues = warnings, |
| checkCompilation = true, |
| api = api, |
| extraArguments = extraArguments, |
| docStubs = docStubs, |
| includeSourceRetentionAnnotations = includeSourceRetentionAnnotations, |
| skipEmitPackages = skipEmitPackages |
| ) |
| } |
| |
| @Test |
| fun `Test package to package info`() { |
| @Language("HTML") |
| val html = """ |
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> |
| <!-- not a body tag: <body> --> |
| <html> |
| <body bgcolor="white"> |
| My package docs<br> |
| <!-- comment --> |
| Sample code: /** code here */ |
| Another line.<br> |
| </BODY> |
| </html> |
| """ |
| |
| @Suppress("DanglingJavadoc") |
| @Language("JAVA") |
| val java = """ |
| /** |
| * My package docs<br> |
| * <!-- comment --> |
| * Sample code: /** code here */ |
| * Another line.<br> |
| */ |
| """ |
| |
| assertEquals(java.trimIndent() + "\n", packageHtmlToJavadoc(html.trimIndent())) |
| } |
| |
| @Test |
| fun `Relative documentation links in stubs`() { |
| checkStubs( |
| docStubs = false, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import java.io.IOException; |
| import test.pkg2.OtherClass; |
| |
| /** |
| * Blah blah {@link OtherClass} blah blah. |
| * Referencing <b>field</b> {@link OtherClass#foo}, |
| * and referencing method {@link OtherClass#bar(int, |
| * boolean)}. |
| * And relative method reference {@link #baz()}. |
| * And relative field reference {@link #importance}. |
| * Here's an already fully qualified reference: {@link test.pkg2.OtherClass}. |
| * And here's one in the same package: {@link LocalClass}. |
| * |
| * @deprecated For some reason |
| * @see OtherClass |
| * @see OtherClass#bar(int, boolean) |
| */ |
| @SuppressWarnings("all") |
| public class SomeClass { |
| /** |
| * My method. |
| * @param focus The focus to find. One of {@link OtherClass#FOCUS_INPUT} or |
| * {@link OtherClass#FOCUS_ACCESSIBILITY}. |
| * @throws IOException when blah blah blah |
| * @throws {@link RuntimeException} when blah blah blah |
| */ |
| public void baz(int focus) throws IOException; |
| public boolean importance; |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg2; |
| |
| @SuppressWarnings("all") |
| public class OtherClass { |
| public static final int FOCUS_INPUT = 1; |
| public static final int FOCUS_ACCESSIBILITY = 2; |
| public int foo; |
| public void bar(int baz, boolean bar); |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg1; |
| |
| @SuppressWarnings("all") |
| public class LocalClass { |
| } |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package test.pkg1; |
| import test.pkg2.OtherClass; |
| import java.io.IOException; |
| /** |
| * Blah blah {@link test.pkg2.OtherClass OtherClass} blah blah. |
| * Referencing <b>field</b> {@link test.pkg2.OtherClass#foo OtherClass#foo}, |
| * and referencing method {@link test.pkg2.OtherClass#bar(int,boolean) OtherClass#bar(int, |
| * boolean)}. |
| * And relative method reference {@link #baz()}. |
| * And relative field reference {@link #importance}. |
| * Here's an already fully qualified reference: {@link test.pkg2.OtherClass}. |
| * And here's one in the same package: {@link test.pkg1.LocalClass LocalClass}. |
| * |
| * @deprecated For some reason |
| * @see test.pkg2.OtherClass |
| * @see test.pkg2.OtherClass#bar(int, boolean) |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| @Deprecated |
| public class SomeClass { |
| public SomeClass() { throw new RuntimeException("Stub!"); } |
| /** |
| * My method. |
| * @param focus The focus to find. One of {@link test.pkg2.OtherClass#FOCUS_INPUT OtherClass#FOCUS_INPUT} or |
| * {@link test.pkg2.OtherClass#FOCUS_ACCESSIBILITY OtherClass#FOCUS_ACCESSIBILITY}. |
| * @throws java.io.IOException when blah blah blah |
| * @throws {@link java.lang.RuntimeException RuntimeException} when blah blah blah |
| */ |
| public void baz(int focus) throws java.io.IOException { throw new RuntimeException("Stub!"); } |
| public boolean importance; |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite relative documentation links in doc-stubs`() { |
| checkStubs( |
| docStubs = true, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import java.io.IOException; |
| import test.pkg2.OtherClass; |
| |
| /** |
| * Blah blah {@link OtherClass} blah blah. |
| * Referencing <b>field</b> {@link OtherClass#foo}, |
| * and referencing method {@link OtherClass#bar(int, |
| * boolean)}. |
| * And relative method reference {@link #baz()}. |
| * And relative field reference {@link #importance}. |
| * Here's an already fully qualified reference: {@link test.pkg2.OtherClass}. |
| * And here's one in the same package: {@link LocalClass}. |
| * |
| * @deprecated For some reason |
| * @see OtherClass |
| * @see OtherClass#bar(int, boolean) |
| */ |
| @SuppressWarnings("all") |
| public class SomeClass { |
| /** |
| * My method. |
| * @param focus The focus to find. One of {@link OtherClass#FOCUS_INPUT} or |
| * {@link OtherClass#FOCUS_ACCESSIBILITY}. |
| * @throws java.io.IOException when blah blah blah |
| * @throws {@link java.lang.RuntimeException} when blah blah blah |
| */ |
| public void baz(int focus) throws IOException; |
| public boolean importance; |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg2; |
| |
| @SuppressWarnings("all") |
| public class OtherClass { |
| public static final int FOCUS_INPUT = 1; |
| public static final int FOCUS_ACCESSIBILITY = 2; |
| public int foo; |
| public void bar(int baz, boolean bar); |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg1; |
| |
| @SuppressWarnings("all") |
| public class LocalClass { |
| } |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package test.pkg1; |
| import test.pkg2.OtherClass; |
| import java.io.IOException; |
| /** |
| * Blah blah {@link test.pkg2.OtherClass OtherClass} blah blah. |
| * Referencing <b>field</b> {@link test.pkg2.OtherClass#foo OtherClass#foo}, |
| * and referencing method {@link test.pkg2.OtherClass#bar(int,boolean) OtherClass#bar(int, |
| * boolean)}. |
| * And relative method reference {@link #baz()}. |
| * And relative field reference {@link #importance}. |
| * Here's an already fully qualified reference: {@link test.pkg2.OtherClass}. |
| * And here's one in the same package: {@link test.pkg1.LocalClass LocalClass}. |
| * |
| * @deprecated For some reason |
| * @see test.pkg2.OtherClass |
| * @see test.pkg2.OtherClass#bar(int, boolean) |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| @Deprecated |
| public class SomeClass { |
| public SomeClass() { throw new RuntimeException("Stub!"); } |
| /** |
| * My method. |
| * @param focus The focus to find. One of {@link test.pkg2.OtherClass#FOCUS_INPUT OtherClass#FOCUS_INPUT} or |
| * {@link test.pkg2.OtherClass#FOCUS_ACCESSIBILITY OtherClass#FOCUS_ACCESSIBILITY}. |
| * @throws java.io.IOException when blah blah blah |
| * @throws {@link java.lang.RuntimeException} when blah blah blah |
| */ |
| public void baz(int focus) throws java.io.IOException { throw new RuntimeException("Stub!"); } |
| public boolean importance; |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite relative documentation links in doc-stubs 2`() { |
| // Properly handle links to inherited methods |
| checkStubs( |
| docStubs = true, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import java.io.IOException; |
| |
| @SuppressWarnings("all") |
| public class R { |
| public static class attr { |
| /** |
| * Resource identifier to assign to this piece of named meta-data. |
| * The resource identifier can later be retrieved from the meta data |
| * Bundle through {@link android.os.Bundle#getInt Bundle.getInt}. |
| * <p>May be a reference to another resource, in the form |
| * "<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>" or a theme |
| * attribute in the form |
| * "<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>". |
| */ |
| public static final int resource=0x01010025; |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.os; |
| |
| @SuppressWarnings("all") |
| public class Bundle extends BaseBundle { |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.os; |
| |
| @SuppressWarnings("all") |
| public class BaseBundle { |
| public int getInt(String key) { |
| return getInt(key, 0); |
| } |
| |
| public int getInt(String key, int defaultValue) { |
| return defaultValue; |
| } |
| } |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package test.pkg1; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class R { |
| public R() { throw new RuntimeException("Stub!"); } |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public static class attr { |
| public attr() { throw new RuntimeException("Stub!"); } |
| /** |
| * Resource identifier to assign to this piece of named meta-data. |
| * The resource identifier can later be retrieved from the meta data |
| * Bundle through {@link android.os.Bundle#getInt Bundle.getInt}. |
| * <p>May be a reference to another resource, in the form |
| * "<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>" or a theme |
| * attribute in the form |
| * "<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>". |
| */ |
| public static final int resource = 16842789; // 0x1010025 |
| } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite relative documentation links in doc-stubs 3`() { |
| checkStubs( |
| docStubs = true, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package android.accessibilityservice; |
| |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityRecord; |
| |
| /** |
| * <p> |
| * Window content may be retrieved with |
| * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()}. |
| * Mention AccessibilityRecords here. |
| * </p> |
| */ |
| @SuppressWarnings("all") |
| public abstract class AccessibilityService { |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.view.accessibility; |
| |
| @SuppressWarnings("all") |
| public final class AccessibilityEvent extends AccessibilityRecord { |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.view.accessibility; |
| |
| @SuppressWarnings("all") |
| public class AccessibilityRecord { |
| public AccessibilityNodeInfo getSource() { |
| return null; |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.view.accessibility; |
| public class AccessibilityNodeInfo {} |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package android.accessibilityservice; |
| import android.view.accessibility.AccessibilityEvent; |
| /** |
| * <p> |
| * Window content may be retrieved with |
| * {@link android.view.accessibility.AccessibilityEvent#getSource() AccessibilityEvent.getSource()}. |
| * Mention AccessibilityRecords here. |
| * </p> |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public abstract class AccessibilityService { |
| public AccessibilityService() { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite relative documentation links in doc-stubs but preserve custom link text`() { |
| checkStubs( |
| docStubs = true, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package android.accessibilityservice; |
| |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityRecord; |
| |
| /** |
| * <p> |
| * Window content may be retrieved with |
| * {@link AccessibilityEvent#getSource() this_method}. |
| * Mention AccessibilityRecords here. |
| * </p> |
| */ |
| @SuppressWarnings("all") |
| public abstract class AccessibilityService { |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.view.accessibility; |
| |
| @SuppressWarnings("all") |
| public final class AccessibilityEvent extends AccessibilityRecord { |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.view.accessibility; |
| |
| @SuppressWarnings("all") |
| public class AccessibilityRecord { |
| public AccessibilityNodeInfo getSource() { |
| return null; |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.view.accessibility; |
| public class AccessibilityNodeInfo {} |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package android.accessibilityservice; |
| import android.view.accessibility.AccessibilityEvent; |
| /** |
| * <p> |
| * Window content may be retrieved with |
| * {@link android.view.accessibility.AccessibilityEvent#getSource() this_method}. |
| * Mention AccessibilityRecords here. |
| * </p> |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public abstract class AccessibilityService { |
| public AccessibilityService() { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite relative documentation links in doc-stubs 4`() { |
| checkStubs( |
| docStubs = true, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package android.content; |
| |
| import android.os.OperationCanceledException; |
| |
| @SuppressWarnings("all") |
| public abstract class AsyncTaskLoader<D> { |
| /** |
| * Called if the task was canceled before it was completed. Gives the class a chance |
| * to clean up post-cancellation and to properly dispose of the result. |
| * |
| * @param data The value that was returned by {@link #loadInBackground}, or null |
| * if the task threw {@link OperationCanceledException}. |
| */ |
| public void onCanceled(D data) { |
| } |
| |
| /** |
| * Called on a worker thread to perform the actual load and to return |
| * the result of the load operation. |
| * |
| * Implementations should not deliver the result directly, but should return them |
| * from this method, which will eventually end up calling {@link #deliverResult} on |
| * the UI thread. If implementations need to process the results on the UI thread |
| * they may override {@link #deliverResult} and do so there. |
| * |
| * When the load is canceled, this method may either return normally or throw |
| * {@link OperationCanceledException}. In either case, the Loader will |
| * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the |
| * result object, if any. |
| * |
| * @return The result of the load operation. |
| * |
| * @throws OperationCanceledException if the load is canceled during execution. |
| * |
| * @see #onCanceled |
| */ |
| public abstract Object loadInBackground(); |
| |
| /** |
| * Sends the result of the load to the registered listener. Should only be called by subclasses. |
| * |
| * Must be called from the process's main thread. |
| * |
| * @param data the result of the load |
| */ |
| public void deliverResult(Object data) { |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.os; |
| |
| |
| /** |
| * An exception type that is thrown when an operation in progress is canceled. |
| */ |
| @SuppressWarnings("all") |
| public class OperationCanceledException extends RuntimeException { |
| public OperationCanceledException() { |
| this(null); |
| } |
| |
| public OperationCanceledException(String message) { |
| super(message != null ? message : "The operation has been canceled."); |
| } |
| } |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package android.content; |
| import android.os.OperationCanceledException; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public abstract class AsyncTaskLoader<D> { |
| public AsyncTaskLoader() { throw new RuntimeException("Stub!"); } |
| /** |
| * Called if the task was canceled before it was completed. Gives the class a chance |
| * to clean up post-cancellation and to properly dispose of the result. |
| * |
| * @param data The value that was returned by {@link #loadInBackground}, or null |
| * if the task threw {@link android.os.OperationCanceledException OperationCanceledException}. |
| */ |
| public void onCanceled(D data) { throw new RuntimeException("Stub!"); } |
| /** |
| * Called on a worker thread to perform the actual load and to return |
| * the result of the load operation. |
| * |
| * Implementations should not deliver the result directly, but should return them |
| * from this method, which will eventually end up calling {@link #deliverResult} on |
| * the UI thread. If implementations need to process the results on the UI thread |
| * they may override {@link #deliverResult} and do so there. |
| * |
| * When the load is canceled, this method may either return normally or throw |
| * {@link android.os.OperationCanceledException OperationCanceledException}. In either case, the Loader will |
| * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the |
| * result object, if any. |
| * |
| * @return The result of the load operation. |
| * |
| * @throws android.os.OperationCanceledException if the load is canceled during execution. |
| * |
| * @see #onCanceled |
| */ |
| public abstract java.lang.Object loadInBackground(); |
| /** |
| * Sends the result of the load to the registered listener. Should only be called by subclasses. |
| * |
| * Must be called from the process's main thread. |
| * |
| * @param data the result of the load |
| */ |
| public void deliverResult(java.lang.Object data) { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite relative documentation links in doc-stubs 5`() { |
| // Properly handle links to inherited methods |
| checkStubs( |
| docStubs = true, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package org.xmlpull.v1; |
| |
| /** |
| * Example docs. |
| * <pre> |
| * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException</a>; |
| * xpp.<a href="#setInput">setInput</a>( new StringReader ( "<foo>Hello World!</foo>" ) ); |
| * </pre> |
| * see #setInput |
| */ |
| @SuppressWarnings("all") |
| public interface XmlPullParser { |
| void setInput(); |
| } |
| """ |
| ) |
| ), |
| warnings = "", |
| source = """ |
| package org.xmlpull.v1; |
| /** |
| * Example docs. |
| * <pre> |
| * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException</a>; |
| * xpp.<a href="#setInput">setInput</a>( new StringReader ( "<foo>Hello World!</foo>" ) ); |
| * </pre> |
| * see #setInput |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public interface XmlPullParser { |
| public void setInput(); |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Check references to inherited field constants`() { |
| checkStubs( |
| docStubs = true, |
| compatibilityMode = false, |
| warnings = "", |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import test.pkg2.MyChild; |
| |
| /** |
| * Reference to {@link MyChild#CONSTANT1}, |
| * {@link MyChild#CONSTANT2}, and |
| * {@link MyChild#myMethod}. |
| * <p> |
| * Absolute reference: |
| * {@link test.pkg2.MyChild#CONSTANT1 MyChild.CONSTANT1} |
| * <p> |
| * Inner class reference: |
| * {@link Test.TestInner#CONSTANT3}, again |
| * {@link TestInner#CONSTANT3} |
| * |
| * @see test.pkg2.MyChild#myMethod |
| */ |
| @SuppressWarnings("all") |
| public class Test { |
| public static class TestInner { |
| public static final String CONSTANT3 = "Hello"; |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg1; |
| @SuppressWarnings("all") |
| interface MyConstants { |
| long CONSTANT1 = 12345; |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg1; |
| import java.io.Closeable; |
| @SuppressWarnings("all") |
| class MyParent implements MyConstants, Closeable { |
| public static final long CONSTANT2 = 67890; |
| public void myMethod() { |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg2; |
| |
| import test.pkg1.MyParent; |
| @SuppressWarnings("all") |
| public class MyChild extends MyParent implements MyConstants { |
| @Override |
| public void close() {} |
| } |
| """ |
| ) |
| ), |
| source = """ |
| package test.pkg1; |
| import test.pkg2.MyChild; |
| /** |
| * Reference to {@link test.pkg2.MyChild#CONSTANT1 MyChild#CONSTANT1}, |
| * {@link test.pkg2.MyChild#CONSTANT2 MyChild#CONSTANT2}, and |
| * {@link test.pkg2.MyChild#myMethod MyChild#myMethod}. |
| * <p> |
| * Absolute reference: |
| * {@link test.pkg2.MyChild#CONSTANT1 MyChild.CONSTANT1} |
| * <p> |
| * Inner class reference: |
| * {@link test.pkg1.Test.TestInner#CONSTANT3 Test.TestInner#CONSTANT3}, again |
| * {@link test.pkg1.Test.TestInner#CONSTANT3 TestInner#CONSTANT3} |
| * |
| * @see test.pkg2.MyChild#myMethod |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Test { |
| public Test() { throw new RuntimeException("Stub!"); } |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public static class TestInner { |
| public TestInner() { throw new RuntimeException("Stub!"); } |
| public static final java.lang.String CONSTANT3 = "Hello"; |
| } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Handle @attr references`() { |
| checkStubs( |
| docStubs = true, |
| compatibilityMode = false, |
| warnings = "", |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| |
| @SuppressWarnings("all") |
| public class Test { |
| /** |
| * Returns the drawable that will be drawn between each item in the list. |
| * |
| * @return the current drawable drawn between list elements |
| * This value may be {@code null}. |
| * @attr ref R.styleable#ListView_divider |
| */ |
| public Object getFoo() { |
| return null; |
| } |
| } |
| """ |
| ) |
| ), |
| source = """ |
| package test.pkg1; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Test { |
| public Test() { throw new RuntimeException("Stub!"); } |
| /** |
| * Returns the drawable that will be drawn between each item in the list. |
| * |
| * @return the current drawable drawn between list elements |
| * This value may be {@code null}. |
| * @attr ref android.R.styleable#ListView_divider |
| */ |
| public java.lang.Object getFoo() { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite parameter list`() { |
| checkStubs( |
| docStubs = true, |
| compatibilityMode = false, |
| warnings = "", |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import test.pkg2.OtherClass1; |
| import test.pkg2.OtherClass2; |
| |
| /** |
| * Reference to {@link OtherClass1#myMethod(OtherClass2, int name, OtherClass2[])}, |
| */ |
| @SuppressWarnings("all") |
| public class Test<E extends OtherClass2> { |
| /** |
| * Reference to {@link OtherClass1#myMethod(E, int, OtherClass2 [])}, |
| */ |
| public void test() { } |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg2; |
| |
| @SuppressWarnings("all") |
| class OtherClass1 { |
| public void myMethod(OtherClass2 parameter1, int parameter2, OtherClass2[] parameter3) { |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg2; |
| |
| @SuppressWarnings("all") |
| public class OtherClass2 { |
| } |
| """ |
| ) |
| ), |
| source = """ |
| package test.pkg1; |
| import test.pkg2.OtherClass2; |
| /** |
| * Reference to {@link test.pkg2.OtherClass1#myMethod(test.pkg2.OtherClass2,int name,test.pkg2.OtherClass2[]) OtherClass1#myMethod(OtherClass2, int name, OtherClass2[])}, |
| */ |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Test<E extends test.pkg2.OtherClass2> { |
| public Test() { throw new RuntimeException("Stub!"); } |
| /** |
| * Reference to {@link test.pkg2.OtherClass1#myMethod(E,int,test.pkg2.OtherClass2[]) OtherClass1#myMethod(E, int, OtherClass2 [])}, |
| */ |
| public void test() { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Rewrite parameter list 2`() { |
| checkStubs( |
| docStubs = true, |
| compatibilityMode = false, |
| warnings = "", |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import java.nio.ByteBuffer; |
| |
| @SuppressWarnings("all") |
| public abstract class Test { |
| /** |
| * Blah blah |
| * <blockquote><pre> |
| * {@link #wrap(ByteBuffer [], int, int, ByteBuffer) |
| * engine.wrap(new ByteBuffer [] { src }, 0, 1, dst);} |
| * </pre></blockquote> |
| */ |
| public void test() { } |
| |
| public abstract void wrap(ByteBuffer [] srcs, int offset, |
| int length, ByteBuffer dst); |
| } |
| """ |
| ) |
| ), |
| source = """ |
| package test.pkg1; |
| import java.nio.ByteBuffer; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public abstract class Test { |
| public Test() { throw new RuntimeException("Stub!"); } |
| /** |
| * Blah blah |
| * <blockquote><pre> |
| * {@link #wrap(java.nio.ByteBuffer[],int,int,java.nio.ByteBuffer) |
| * engine.wrap(new ByteBuffer [] { src }, 0, 1, dst);} |
| * </pre></blockquote> |
| */ |
| public void test() { throw new RuntimeException("Stub!"); } |
| public abstract void wrap(java.nio.ByteBuffer[] srcs, int offset, int length, java.nio.ByteBuffer dst); |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Warn about unresolved`() { |
| @Suppress("ConstantConditionIf") |
| checkStubs( |
| docStubs = true, |
| compatibilityMode = false, |
| warnings = |
| if (REPORT_UNRESOLVED_SYMBOLS) { |
| """ |
| src/test/pkg1/Test.java:6: lint: Unresolved documentation reference: SomethingMissing [UnresolvedLink] |
| src/test/pkg1/Test.java:6: lint: Unresolved documentation reference: OtherMissing [UnresolvedLink] |
| """ |
| } else { |
| "" |
| }, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg1; |
| import java.nio.ByteBuffer; |
| |
| @SuppressWarnings("all") |
| public class Test { |
| /** |
| * Reference to {@link SomethingMissing} and |
| * {@link String#randomMethod}. |
| * |
| * @see OtherMissing |
| */ |
| public void test() { } |
| } |
| """ |
| ) |
| ), |
| source = """ |
| package test.pkg1; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Test { |
| public Test() { throw new RuntimeException("Stub!"); } |
| /** |
| * Reference to {@link SomethingMissing} and |
| * {@link java.lang.String#randomMethod String#randomMethod}. |
| * |
| * @see OtherMissing |
| */ |
| public void test() { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| } |
| |
| @Test |
| fun `Javadoc link to innerclass constructor`() { |
| check( |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package android.view; |
| import android.graphics.Insets; |
| |
| public final class WindowInsets { |
| /** |
| * Returns a copy of this WindowInsets with selected system window insets replaced |
| * with new values. |
| * |
| * @param left New left inset in pixels |
| * @param top New top inset in pixels |
| * @param right New right inset in pixels |
| * @param bottom New bottom inset in pixels |
| * @return A modified copy of this WindowInsets |
| * @deprecated use {@link Builder#Builder(WindowInsets)} with |
| * {@link Builder#setSystemWindowInsets(Insets)} instead. |
| */ |
| @Deprecated |
| public WindowInsets replaceSystemWindowInsets(int left, int top, int right, int bottom) { |
| |
| } |
| |
| public static class Builder { |
| public Builder() { |
| } |
| |
| public Builder(WindowInsets insets) { |
| } |
| |
| public Builder setSystemWindowInsets(Insets systemWindowInsets) { |
| return this; |
| } |
| } |
| } |
| """ |
| ), |
| java( |
| """ |
| package android.graphics; |
| public class Insets { |
| } |
| """ |
| ) |
| ), |
| docStubs = true, |
| stubs = arrayOf( |
| """ |
| package android.view; |
| import android.graphics.Insets; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public final class WindowInsets { |
| public WindowInsets() { throw new RuntimeException("Stub!"); } |
| /** |
| * Returns a copy of this WindowInsets with selected system window insets replaced |
| * with new values. |
| * |
| * @param left New left inset in pixels |
| * @param top New top inset in pixels |
| * @param right New right inset in pixels |
| * @param bottom New bottom inset in pixels |
| * @return A modified copy of this WindowInsets |
| * @deprecated use {@link android.view.WindowInsets.Builder#Builder(android.view.WindowInsets) Builder#Builder(WindowInsets)} with |
| * {@link android.view.WindowInsets.Builder#setSystemWindowInsets(android.graphics.Insets) Builder#setSystemWindowInsets(Insets)} instead. |
| */ |
| @Deprecated |
| public android.view.WindowInsets replaceSystemWindowInsets(int left, int top, int right, int bottom) { throw new RuntimeException("Stub!"); } |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public static class Builder { |
| public Builder() { throw new RuntimeException("Stub!"); } |
| public Builder(android.view.WindowInsets insets) { throw new RuntimeException("Stub!"); } |
| public android.view.WindowInsets.Builder setSystemWindowInsets(android.graphics.Insets systemWindowInsets) { throw new RuntimeException("Stub!"); } |
| } |
| } |
| """ |
| ) |
| ) |
| } |
| |
| @Test |
| fun `Ensure references to classes in JavaDoc of hidden members do not affect imports`() { |
| check( |
| compatibilityMode = false, |
| sourceFiles = arrayOf( |
| java( |
| """ |
| package test.pkg; |
| import test.pkg.bar.Bar; |
| import test.pkg.baz.Baz; |
| public class Foo { |
| /** |
| * This method is hidden so the reference to {@link Baz} in this comment |
| * should not cause test.pkg.baz.Baz import to be added even though Baz is |
| * part of the API. |
| * @hide |
| */ |
| public void baz() {} |
| |
| /** |
| * @see Bar |
| */ |
| public void bar() {} |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg.bar; |
| import test.pkg.Foo; |
| import test.pkg.baz.Baz; |
| public class Bar { |
| /** @see Baz */ |
| public void baz(Baz baz) {} |
| /** @see Foo */ |
| public void foo(Foo foo) {} |
| } |
| """ |
| ), |
| java( |
| """ |
| package test.pkg.baz; |
| public class Baz { |
| } |
| """ |
| ) |
| ), |
| stubs = arrayOf( |
| """ |
| package test.pkg; |
| import test.pkg.bar.Bar; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Foo { |
| public Foo() { throw new RuntimeException("Stub!"); } |
| /** |
| * @see test.pkg.bar.Bar |
| */ |
| public void bar() { throw new RuntimeException("Stub!"); } |
| } |
| """, |
| """ |
| package test.pkg.bar; |
| import test.pkg.baz.Baz; |
| import test.pkg.Foo; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Bar { |
| public Bar() { throw new RuntimeException("Stub!"); } |
| /** @see test.pkg.baz.Baz */ |
| public void baz(test.pkg.baz.Baz baz) { throw new RuntimeException("Stub!"); } |
| /** @see test.pkg.Foo */ |
| public void foo(test.pkg.Foo foo) { throw new RuntimeException("Stub!"); } |
| } |
| """, |
| """ |
| package test.pkg.baz; |
| @SuppressWarnings({"unchecked", "deprecation", "all"}) |
| public class Baz { |
| public Baz() { throw new RuntimeException("Stub!"); } |
| } |
| """ |
| ) |
| ) |
| } |
| } |