1 /* 2 * Copyright (C) 2023 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.doc 18 19 import com.android.tools.metalava.DriverTest 20 import com.android.tools.metalava.intDefAnnotationSource 21 import com.android.tools.metalava.intRangeAnnotationSource 22 import com.android.tools.metalava.testing.java 23 import org.junit.Test 24 25 /** Tests for the [DocAnalyzer] which check the handling of ranges in the docs */ 26 class DocAnalyzerRangeTest : DriverTest() { 27 @Test Document rangesnull28 fun `Document ranges`() { 29 check( 30 sourceFiles = 31 arrayOf( 32 java( 33 """ 34 package test.pkg; 35 36 import android.Manifest; 37 import android.annotation.IntRange; 38 39 public class RangeTest { 40 @IntRange(from = 10) 41 public int test1(@IntRange(from = 20) int range2) { return 15; } 42 43 @IntRange(from = 10, to = 20) 44 public int test2() { return 15; } 45 46 @IntRange(to = 100) 47 public int test3() { return 50; } 48 } 49 """ 50 ), 51 intRangeAnnotationSource 52 ), 53 docStubs = true, 54 checkCompilation = true, 55 stubFiles = 56 arrayOf( 57 java( 58 """ 59 package test.pkg; 60 @SuppressWarnings({"unchecked", "deprecation", "all"}) 61 public class RangeTest { 62 public RangeTest() { throw new RuntimeException("Stub!"); } 63 /** 64 * @param range2 Value is 20 or greater 65 * @return Value is 10 or greater 66 */ 67 public int test1(int range2) { throw new RuntimeException("Stub!"); } 68 /** 69 * @return Value is between 10 and 20 inclusive 70 */ 71 public int test2() { throw new RuntimeException("Stub!"); } 72 /** 73 * @return Value is 100 or less 74 */ 75 public int test3() { throw new RuntimeException("Stub!"); } 76 } 77 """ 78 ) 79 ) 80 ) 81 } 82 83 @Test Typedefsnull84 fun Typedefs() { 85 check( 86 sourceFiles = 87 arrayOf( 88 java( 89 """ 90 package test.pkg; 91 92 import android.annotation.IntDef; 93 import android.annotation.IntRange; 94 95 import java.lang.annotation.Retention; 96 import java.lang.annotation.RetentionPolicy; 97 98 @SuppressWarnings({"UnusedDeclaration", "WeakerAccess"}) 99 public class TypedefTest { 100 @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT}) 101 @Retention(RetentionPolicy.SOURCE) 102 private @interface DialogStyle {} 103 104 public static final int STYLE_NORMAL = 0; 105 public static final int STYLE_NO_TITLE = 1; 106 public static final int STYLE_NO_FRAME = 2; 107 public static final int STYLE_NO_INPUT = 3; 108 public static final int STYLE_UNRELATED = 3; 109 110 public void setStyle(@DialogStyle int style, int theme) { 111 } 112 113 @IntDef(value = {STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 2, 3 + 1}, 114 flag=true) 115 @Retention(RetentionPolicy.SOURCE) 116 private @interface DialogFlags {} 117 118 public void setFlags(Object first, @DialogFlags int flags) { 119 } 120 } 121 """ 122 ), 123 intRangeAnnotationSource, 124 intDefAnnotationSource 125 ), 126 checkCompilation = true, 127 docStubs = true, 128 stubFiles = 129 arrayOf( 130 java( 131 """ 132 package test.pkg; 133 @SuppressWarnings({"unchecked", "deprecation", "all"}) 134 public class TypedefTest { 135 public TypedefTest() { throw new RuntimeException("Stub!"); } 136 /** 137 * @param flags Value is either <code>0</code> or a combination of {@link test.pkg.TypedefTest#STYLE_NORMAL}, {@link test.pkg.TypedefTest#STYLE_NO_TITLE}, {@link test.pkg.TypedefTest#STYLE_NO_FRAME}, {@link test.pkg.TypedefTest#STYLE_NO_INPUT}, 2, and 3 + 1 138 */ 139 public void setFlags(java.lang.Object first, int flags) { throw new RuntimeException("Stub!"); } 140 /** 141 * @param style Value is {@link test.pkg.TypedefTest#STYLE_NORMAL}, {@link test.pkg.TypedefTest#STYLE_NO_TITLE}, {@link test.pkg.TypedefTest#STYLE_NO_FRAME}, or {@link test.pkg.TypedefTest#STYLE_NO_INPUT} 142 */ 143 public void setStyle(int style, int theme) { throw new RuntimeException("Stub!"); } 144 public static final int STYLE_NORMAL = 0; // 0x0 145 public static final int STYLE_NO_FRAME = 2; // 0x2 146 public static final int STYLE_NO_INPUT = 3; // 0x3 147 public static final int STYLE_NO_TITLE = 1; // 0x1 148 public static final int STYLE_UNRELATED = 3; // 0x3 149 } 150 """ 151 ) 152 ) 153 ) 154 } 155 156 @Test Typedefs combined with rangesnull157 fun `Typedefs combined with ranges`() { 158 check( 159 sourceFiles = 160 arrayOf( 161 java( 162 """ 163 package test.pkg; 164 165 import android.annotation.IntDef; 166 import android.annotation.IntRange; 167 168 import java.lang.annotation.Retention; 169 import java.lang.annotation.RetentionPolicy; 170 171 @SuppressWarnings({"UnusedDeclaration", "WeakerAccess"}) 172 public class TypedefTest { 173 @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT}) 174 @IntRange(from = 20) 175 @Retention(RetentionPolicy.SOURCE) 176 private @interface DialogStyle {} 177 178 public static final int STYLE_NORMAL = 0; 179 public static final int STYLE_NO_TITLE = 1; 180 public static final int STYLE_NO_FRAME = 2; 181 182 public void setStyle(@DialogStyle int style, int theme) { 183 } 184 } 185 """ 186 ), 187 intRangeAnnotationSource, 188 intDefAnnotationSource 189 ), 190 docStubs = true, 191 checkCompilation = true, 192 stubFiles = 193 arrayOf( 194 java( 195 """ 196 package test.pkg; 197 @SuppressWarnings({"unchecked", "deprecation", "all"}) 198 public class TypedefTest { 199 public TypedefTest() { throw new RuntimeException("Stub!"); } 200 /** 201 * @param style Value is {@link test.pkg.TypedefTest#STYLE_NORMAL}, {@link test.pkg.TypedefTest#STYLE_NO_TITLE}, {@link test.pkg.TypedefTest#STYLE_NO_FRAME}, or STYLE_NO_INPUT 202 * Value is 20 or greater 203 */ 204 public void setStyle(int style, int theme) { throw new RuntimeException("Stub!"); } 205 public static final int STYLE_NORMAL = 0; // 0x0 206 public static final int STYLE_NO_FRAME = 2; // 0x2 207 public static final int STYLE_NO_TITLE = 1; // 0x1 208 } 209 """ 210 ) 211 ) 212 ) 213 } 214 215 @Test Add new parameter when no doc existsnull216 fun `Add new parameter when no doc exists`() { 217 check( 218 sourceFiles = 219 arrayOf( 220 java( 221 """ 222 package test.pkg; 223 import android.annotation.IntRange; 224 public class RangeTest { 225 public int test1(int parameter1, @IntRange(from = 10) int parameter2, int parameter3) { } 226 } 227 """ 228 ), 229 intRangeAnnotationSource 230 ), 231 checkCompilation = true, 232 docStubs = true, 233 stubFiles = 234 arrayOf( 235 java( 236 """ 237 package test.pkg; 238 @SuppressWarnings({"unchecked", "deprecation", "all"}) 239 public class RangeTest { 240 public RangeTest() { throw new RuntimeException("Stub!"); } 241 /** 242 * @param parameter2 Value is 10 or greater 243 */ 244 public int test1(int parameter1, int parameter2, int parameter3) { throw new RuntimeException("Stub!"); } 245 } 246 """ 247 ) 248 ) 249 ) 250 } 251 252 @Test Add new parameter when doc exists but no param docnull253 fun `Add new parameter when doc exists but no param doc`() { 254 check( 255 sourceFiles = 256 arrayOf( 257 java( 258 """ 259 package test.pkg; 260 import android.annotation.IntRange; 261 public class RangeTest { 262 /** 263 * This is the existing documentation. 264 * @return return value documented here 265 */ 266 public int test1(int parameter1, @IntRange(from = 10) int parameter2, int parameter3) { } 267 } 268 """ 269 ), 270 intRangeAnnotationSource 271 ), 272 checkCompilation = true, 273 docStubs = true, 274 stubFiles = 275 arrayOf( 276 java( 277 """ 278 package test.pkg; 279 @SuppressWarnings({"unchecked", "deprecation", "all"}) 280 public class RangeTest { 281 public RangeTest() { throw new RuntimeException("Stub!"); } 282 /** 283 * This is the existing documentation. 284 * @param parameter2 Value is 10 or greater 285 * @return return value documented here 286 */ 287 public int test1(int parameter1, int parameter2, int parameter3) { throw new RuntimeException("Stub!"); } 288 } 289 """ 290 ) 291 ) 292 ) 293 } 294 295 @Test Add new parameter, sorted correctly between existing onesnull296 fun `Add new parameter, sorted correctly between existing ones`() { 297 check( 298 sourceFiles = 299 arrayOf( 300 java( 301 """ 302 package test.pkg; 303 import android.annotation.IntRange; 304 public class RangeTest { 305 /** 306 * This is the existing documentation. 307 * @param parameter1 docs for parameter1 308 * @param parameter3 docs for parameter2 309 * @return return value documented here 310 */ 311 public int test1(int parameter1, @IntRange(from = 10) int parameter2, int parameter3) { } 312 } 313 """ 314 ), 315 intRangeAnnotationSource 316 ), 317 checkCompilation = true, 318 docStubs = true, 319 stubFiles = 320 arrayOf( 321 java( 322 """ 323 package test.pkg; 324 @SuppressWarnings({"unchecked", "deprecation", "all"}) 325 public class RangeTest { 326 public RangeTest() { throw new RuntimeException("Stub!"); } 327 /** 328 * This is the existing documentation. 329 * @param parameter1 docs for parameter1 330 * @param parameter3 docs for parameter2 331 * @param parameter2 Value is 10 or greater 332 * @return return value documented here 333 */ 334 public int test1(int parameter1, int parameter2, int parameter3) { throw new RuntimeException("Stub!"); } 335 } 336 """ 337 ) 338 ) 339 ) 340 } 341 342 @Test Add to existing parameternull343 fun `Add to existing parameter`() { 344 check( 345 sourceFiles = 346 arrayOf( 347 java( 348 """ 349 package test.pkg; 350 import android.annotation.IntRange; 351 public class RangeTest { 352 /** 353 * This is the existing documentation. 354 * @param parameter1 docs for parameter1 355 * @param parameter2 docs for parameter2 356 * @param parameter3 docs for parameter2 357 * @return return value documented here 358 */ 359 public int test1(int parameter1, @IntRange(from = 10) int parameter2, int parameter3) { } 360 } 361 """ 362 ), 363 intRangeAnnotationSource 364 ), 365 checkCompilation = true, 366 docStubs = true, 367 stubFiles = 368 arrayOf( 369 java( 370 """ 371 package test.pkg; 372 @SuppressWarnings({"unchecked", "deprecation", "all"}) 373 public class RangeTest { 374 public RangeTest() { throw new RuntimeException("Stub!"); } 375 /** 376 * This is the existing documentation. 377 * @param parameter1 docs for parameter1 378 * @param parameter2 docs for parameter2 379 * Value is 10 or greater 380 * @param parameter3 docs for parameter2 381 * @return return value documented here 382 */ 383 public int test1(int parameter1, int parameter2, int parameter3) { throw new RuntimeException("Stub!"); } 384 } 385 """ 386 ) 387 ) 388 ) 389 } 390 391 @Test Add new return valuenull392 fun `Add new return value`() { 393 check( 394 sourceFiles = 395 arrayOf( 396 java( 397 """ 398 package test.pkg; 399 import android.annotation.IntRange; 400 public class RangeTest { 401 @IntRange(from = 10) 402 public int test1(int parameter1, int parameter2, int parameter3) { } 403 } 404 """ 405 ), 406 intRangeAnnotationSource 407 ), 408 checkCompilation = true, 409 docStubs = true, 410 stubFiles = 411 arrayOf( 412 java( 413 """ 414 package test.pkg; 415 @SuppressWarnings({"unchecked", "deprecation", "all"}) 416 public class RangeTest { 417 public RangeTest() { throw new RuntimeException("Stub!"); } 418 /** 419 * @return Value is 10 or greater 420 */ 421 public int test1(int parameter1, int parameter2, int parameter3) { throw new RuntimeException("Stub!"); } 422 } 423 """ 424 ) 425 ) 426 ) 427 } 428 429 @Test Add to existing return value (ensuring it appears last)null430 fun `Add to existing return value (ensuring it appears last)`() { 431 check( 432 sourceFiles = 433 arrayOf( 434 java( 435 """ 436 package test.pkg; 437 import android.annotation.IntRange; 438 public class RangeTest { 439 /** 440 * This is the existing documentation. 441 * @return return value documented here 442 */ 443 @IntRange(from = 10) 444 public int test1(int parameter1, int parameter2, int parameter3) { } 445 } 446 """ 447 ), 448 intRangeAnnotationSource 449 ), 450 checkCompilation = true, 451 docStubs = true, 452 stubFiles = 453 arrayOf( 454 java( 455 """ 456 package test.pkg; 457 @SuppressWarnings({"unchecked", "deprecation", "all"}) 458 public class RangeTest { 459 public RangeTest() { throw new RuntimeException("Stub!"); } 460 /** 461 * This is the existing documentation. 462 * @return return value documented here 463 * Value is 10 or greater 464 */ 465 public int test1(int parameter1, int parameter2, int parameter3) { throw new RuntimeException("Stub!"); } 466 } 467 """ 468 ) 469 ) 470 ) 471 } 472 473 @Test Merge API levelsnull474 fun `Merge API levels`() { 475 check( 476 sourceFiles = 477 arrayOf( 478 java( 479 """ 480 package android.widget; 481 482 public class Toolbar { 483 /** 484 * Existing documentation for {@linkplain #getCurrentContentInsetEnd()} here. 485 * @return blah blah blah 486 */ 487 public int getCurrentContentInsetEnd() { 488 return 0; 489 } 490 } 491 """ 492 ), 493 intRangeAnnotationSource 494 ), 495 checkCompilation = true, 496 docStubs = true, 497 applyApiLevelsXml = 498 """ 499 <?xml version="1.0" encoding="utf-8"?> 500 <api version="2"> 501 <class name="android/widget/Toolbar" since="21"> 502 <method name="<init>(Landroid/content/Context;)V"/> 503 <method name="collapseActionView()V"/> 504 <method name="getContentInsetStartWithNavigation()I" since="24"/> 505 <method name="getCurrentContentInsetEnd()I" since="24"/> 506 <method name="getCurrentContentInsetLeft()I" since="24"/> 507 <method name="getCurrentContentInsetRight()I" since="24"/> 508 <method name="getCurrentContentInsetStart()I" since="24"/> 509 </class> 510 </api> 511 """, 512 stubFiles = 513 arrayOf( 514 java( 515 """ 516 package android.widget; 517 /** @apiSince 21 */ 518 @SuppressWarnings({"unchecked", "deprecation", "all"}) 519 public class Toolbar { 520 public Toolbar() { throw new RuntimeException("Stub!"); } 521 /** 522 * Existing documentation for {@linkplain #getCurrentContentInsetEnd()} here. 523 * @return blah blah blah 524 * @apiSince 24 525 */ 526 public int getCurrentContentInsetEnd() { throw new RuntimeException("Stub!"); } 527 } 528 """ 529 ) 530 ) 531 ) 532 } 533 534 @Test Trailing comment closenull535 fun `Trailing comment close`() { 536 check( 537 sourceFiles = 538 arrayOf( 539 java( 540 """ 541 package android.widget; 542 543 public class Toolbar { 544 /** 545 * Existing documentation for {@linkplain #getCurrentContentInsetEnd()} here. */ 546 public int getCurrentContentInsetEnd() { 547 return 0; 548 } 549 } 550 """ 551 ), 552 intRangeAnnotationSource 553 ), 554 checkCompilation = true, 555 docStubs = true, 556 applyApiLevelsXml = 557 """ 558 <?xml version="1.0" encoding="utf-8"?> 559 <api version="2"> 560 <class name="android/widget/Toolbar" since="21"> 561 <method name="getCurrentContentInsetEnd()I" since="24"/> 562 </class> 563 </api> 564 """, 565 stubFiles = 566 arrayOf( 567 java( 568 """ 569 package android.widget; 570 /** @apiSince 21 */ 571 @SuppressWarnings({"unchecked", "deprecation", "all"}) 572 public class Toolbar { 573 public Toolbar() { throw new RuntimeException("Stub!"); } 574 /** 575 * Existing documentation for {@linkplain #getCurrentContentInsetEnd()} here. 576 * @apiSince 24 577 */ 578 public int getCurrentContentInsetEnd() { throw new RuntimeException("Stub!"); } 579 } 580 """ 581 ) 582 ) 583 ) 584 } 585 } 586