1 /* 2 * Copyright (C) 2022 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.google.doclava.javadoc; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 22 import javax.lang.model.element.ExecutableElement; 23 import javax.lang.model.element.PackageElement; 24 import javax.lang.model.element.TypeElement; 25 import javax.lang.model.element.VariableElement; 26 import javax.lang.model.type.ArrayType; 27 import jdk.javadoc.doclet.DocletEnvironment; 28 import org.junit.Before; 29 import org.junit.BeforeClass; 30 import org.junit.runner.RunWith; 31 import org.junit.runners.JUnit4; 32 33 @RunWith(JUnit4.class) 34 public abstract class BaseTest { 35 36 protected static RootDocImpl rootDoc; 37 protected static DocletEnvironment docletEnv; 38 protected Context context; 39 40 /** 41 * @implNote While marked with {@link BeforeClass}, the actual initialization happens only once 42 * across multiple runs by test subclasses, which results are stored in a singleton manner. 43 */ 44 @BeforeClass beforeClass()45 public static void beforeClass() { 46 if (docletEnv != null && rootDoc != null) { 47 return; 48 } 49 50 var doclet = new EmptyDoclet("src/test/resources"); 51 docletEnv = doclet.getEnvironment(); 52 53 rootDoc = new RootDocImpl(docletEnv); 54 } 55 56 @Before setUp()57 public void setUp() { 58 context = new Context(docletEnv); 59 } 60 61 // TypeElements (ANNOTATION_TYPE, CLASS, ENUM, INTERFACE, or RECORD). 62 static class CLASS { 63 64 static final TypeElement publicAbstractClass = initTypeElement( 65 "com.example.classes.AbstractEmptyClass"); 66 static final TypeElement publicAbstractInterface = initTypeElement( 67 "com.example.classes.AbstractEmptyInterface"); 68 69 static final TypeElement publicAnnotation = initTypeElement( 70 "com.example.classes.PublicAnnotation"); 71 static final TypeElement publicClass = initTypeElement("com.example.classes.PublicClass"); 72 static final TypeElement publicEnum = initTypeElement("com.example.classes.PublicEnum"); 73 static final TypeElement publicInterface = initTypeElement( 74 "com.example.classes.PublicInterface"); 75 76 static final TypeElement simpleEnum = initTypeElement("com.example.classes.SimpleEnum"); 77 78 static final TypeElement publicClassWithNests = initTypeElement( 79 "com.example.classes.PublicClassWithNests"); 80 static final TypeElement publicClassWithNests$Nest1 = initTypeElement( 81 "com.example.classes.PublicClassWithNests.Nest1"); 82 static final TypeElement publicClassWithNests$Nest1$Nest2 = initTypeElement( 83 "com.example.classes.PublicClassWithNests.Nest1.Nest2"); 84 static final TypeElement publicClassWithNests$Nest1$Nest2$Nest3 = initTypeElement( 85 "com.example.classes.PublicClassWithNests.Nest1.Nest2.Nest3"); 86 static final TypeElement innerClasses = initTypeElement( 87 "com.example.classes.InnerClasses"); 88 89 static final TypeElement parametrizedAnnotation = initTypeElement( 90 "com.example.classes.ParametrizedAnnotation"); 91 92 static final TypeElement tags = initTypeElement("com.example.classes.Tags"); 93 static final TypeElement tags$See = initTypeElement("com.example.classes.Tags.See"); 94 static final TypeElement tags$Throws = initTypeElement("com.example.classes.Tags.Throws"); 95 static final TypeElement tags$Various = initTypeElement("com.example.classes.Tags.Various"); 96 97 static final TypeElement constructors = initTypeElement( 98 "com.example.constructors.Constructors"); 99 100 static final TypeElement packagePrivateClass = initTypeElement( 101 "com.example.classes.PackagePrivateClass"); 102 103 static final TypeElement implementsSerializable = initTypeElement( 104 "com.example.classes.ImplementsSerializable"); 105 static final TypeElement implementsExternalizable = initTypeElement( 106 "com.example.classes.ImplementsExternalizable"); 107 108 static final TypeElement annotatedClass = initTypeElement( 109 "com.example.classes.AnnotatedClass"); 110 111 static final TypeElement javaUtilMap = initTypeElement("java.util.Map"); 112 113 static final TypeElement fieldsAccessModifiers = initTypeElement( 114 "com.example.classes.FieldsAccessModifiers"); 115 static final TypeElement methodsAccessModifiers = initTypeElement( 116 "com.example.classes.MethodsAccessModifiers"); 117 } 118 119 static class GENERIC { 120 121 static final TypeElement box = initTypeElement("com.example.classes.Tags.Box"); 122 } 123 124 static class ARRAY { 125 126 static final ArrayType int_1 = (ArrayType) initVariableElement( 127 "com.example.fields.Arrays", "arr_int_1").asType(); 128 static final ArrayType string_2 = (ArrayType) initVariableElement( 129 "com.example.fields.Arrays", "arr_String_2").asType(); 130 static final ArrayType T_3 = (ArrayType) initVariableElement( 131 "com.example.fields.Arrays", "arr_T_3").asType(); 132 static final ArrayType ListOfStrings_4 = (ArrayType) initVariableElement( 133 "com.example.fields.Arrays", "arr_ListOfString_4").asType(); 134 static final ArrayType override_5 = (ArrayType) initVariableElement( 135 "com.example.fields.Arrays", "arr_Override_5").asType(); 136 } 137 138 static class INTERFACE { 139 140 static final TypeElement serializable = initTypeElement("java.io.Serializable"); 141 static final TypeElement extendsSerializable = initTypeElement( 142 "com.example.classes.ExtendsSerializable"); 143 static final TypeElement extendsExternalizable = initTypeElement( 144 "com.example.classes.ExtendsExternalizable"); 145 } 146 147 static class INSTANCE { 148 149 static final TypeElement javaLangObject = initTypeElement("java.lang.Object"); 150 static final TypeElement javaLangError = initTypeElement("java.lang.Error"); 151 static final TypeElement javaLangException = initTypeElement("java.lang.Exception"); 152 static final TypeElement javaLangString = initTypeElement("java.lang.String"); 153 static final TypeElement javaLangThrowable = initTypeElement("java.lang.Throwable"); 154 } 155 156 static class PACKAGE { 157 158 static PackageElement comExamplePackages = initPackageElement("com.example.packages"); 159 } 160 161 static class ANNOTATION_METHOD { 162 163 /** 164 * <pre> 165 * public @interface AllDefaultAnnotation { 166 * boolean bool() default true; 167 * byte byt() default (byte)1; 168 * char ch() default 'a'; 169 * double dbl() default 3.1d; 170 * float flt() default 4.1f; 171 * int integer() default 5; 172 * long lng() default 6L; 173 * short shrt() default (short)7; 174 * String str() default "qwe"; 175 * Class<?> cls() default PublicClass.class; 176 * SimpleEnum enm() default SimpleEnum.A; 177 * Class<?> annotation() default Override.class; 178 * String[] arrayOfStrings() default { "abc", "def", "ghi" }; 179 * } 180 * </pre> 181 */ 182 static class WITH_DEFAULT { 183 184 static final ExecutableElement returningBool = initExecutableElement( 185 "com.example.classes.AllDefaultAnnotation", "bool()"); 186 static final ExecutableElement returningByte = initExecutableElement( 187 "com.example.classes.AllDefaultAnnotation", "byt()"); 188 static final ExecutableElement returningChar = initExecutableElement( 189 "com.example.classes.AllDefaultAnnotation", "ch()"); 190 static final ExecutableElement returningDouble = initExecutableElement( 191 "com.example.classes.AllDefaultAnnotation", "dbl()"); 192 static final ExecutableElement returningFloat = initExecutableElement( 193 "com.example.classes.AllDefaultAnnotation", "flt()"); 194 static final ExecutableElement returningInteger = initExecutableElement( 195 "com.example.classes.AllDefaultAnnotation", "integer()"); 196 static final ExecutableElement returningLond = initExecutableElement( 197 "com.example.classes.AllDefaultAnnotation", "lng()"); 198 static final ExecutableElement returningShort = initExecutableElement( 199 "com.example.classes.AllDefaultAnnotation", "shrt()"); 200 static final ExecutableElement returningString = initExecutableElement( 201 "com.example.classes.AllDefaultAnnotation", "str()"); 202 static final ExecutableElement returningClass = initExecutableElement( 203 "com.example.classes.AllDefaultAnnotation", "cls()"); 204 static final ExecutableElement returningEnum = initExecutableElement( 205 "com.example.classes.AllDefaultAnnotation", "enm()"); 206 static final ExecutableElement returningAnnotation = initExecutableElement( 207 "com.example.classes.AllDefaultAnnotation", "annotation()"); 208 static final ExecutableElement returningArrayOfString = initExecutableElement( 209 "com.example.classes.AllDefaultAnnotation", "arrayOfStrings()"); 210 } 211 212 static final TypeElement allDefaultAnnotation = initTypeElement( 213 "com.example.classes.AllDefaultAnnotation"); 214 static final ExecutableElement annotationMethod = initExecutableElement( 215 "com.example.classes.ParametrizedAnnotation", "primitiveI()"); 216 static final ExecutableElement annotationMethodWithDefault = initExecutableElement( 217 "com.example.classes.ParametrizedAnnotation", "primitiveDefaultL()"); 218 } 219 220 static class CONSTRUCTOR { 221 222 static final ExecutableElement empty = initExecutableElement( 223 "com.example.constructors.Constructors", "Constructors()"); 224 static final ExecutableElement arg1_int = initExecutableElement( 225 "com.example.constructors.Constructors", "Constructors(int)"); 226 static final ExecutableElement arg1_String = initExecutableElement( 227 "com.example.constructors.Constructors", "Constructors(java.lang.String)"); 228 static final ExecutableElement arg2_int_String = initExecutableElement( 229 "com.example.constructors.Constructors", "Constructors(int,java.lang.String)"); 230 231 static final ExecutableElement paramTag_arg2_T_int = initExecutableElement( 232 "com.example.classes.Tags.Box", "Box(T,int)"); 233 } 234 235 static class METHOD { 236 237 static class OF_CLASS { 238 239 static final ExecutableElement public_void_arg0 = initExecutableElement( 240 "com.example.methods.OfClass", "public_void_arg0()"); 241 static final ExecutableElement private_int_arg0 = initExecutableElement( 242 "com.example.methods.OfClass", "private_int_arg0()"); 243 static final ExecutableElement packagePrivate_String_arg2_int_String = initExecutableElement( 244 "com.example.methods.OfClass", 245 "packagePrivate_String_arg2_int_String(int,java.lang.String)"); 246 static final ExecutableElement public_abstract_void_arg0 = initExecutableElement( 247 "com.example.methods.OfClass", "public_abstract_void_arg0()"); 248 static final ExecutableElement override_public_String_toString0 = initExecutableElement( 249 "com.example.methods.OfClass", "toString()"); 250 static final ExecutableElement void_arg1_annotatedObject = initExecutableElement( 251 "com.example.methods.OfClass", 252 "void_arg1_annotatedObject(@com.example.classes.UniversalAnnotation java.lang.Object)"); 253 } 254 255 static class OF_INTERFACE { 256 257 static final ExecutableElement public_void_arg0 = initExecutableElement( 258 "com.example.methods.OfInterface", "public_void_arg0()"); 259 static final ExecutableElement public_default_String_arg0 = initExecutableElement( 260 "com.example.methods.OfInterface", "public_default_String_arg0()"); 261 } 262 263 static class OVERRIDES { 264 265 static final ExecutableElement A_name = initExecutableElement( 266 "com.example.methods.override.A", "name()"); 267 static final ExecutableElement B_name = initExecutableElement( 268 "com.example.methods.override.B", "name()"); 269 static final ExecutableElement C_name = initExecutableElement( 270 "com.example.methods.override.C", "name()"); 271 static final ExecutableElement D_name = initExecutableElement( 272 "com.example.methods.override.D", "name()"); 273 } 274 275 static class PARAMETER { 276 277 static final VariableElement Object_annotatedWith_UniversalAnnotation = initMethodParam( 278 "com.example.methods.OfClass", 279 "void_arg1_annotatedObject(@com.example.classes.UniversalAnnotation java.lang.Object)", 280 "obj"); 281 } 282 } 283 284 static class FIELD { 285 286 static final VariableElement public_int = initVariableElement( 287 "com.example.fields.Fields", "public_int"); 288 static final VariableElement public_transient_volatile_Object = initVariableElement( 289 "com.example.fields.Fields", "public_transient_volatile_Object"); 290 static final VariableElement public_final_int = initVariableElement( 291 "com.example.fields.Fields", "public_final_int"); 292 static final VariableElement public_final_String = initVariableElement( 293 "com.example.fields.Fields", "public_final_String"); 294 295 static class ACCESS_MODIFIERS { 296 297 static final VariableElement public_int = initVariableElement( 298 "com.example.fields.FieldsAccessModifiers", "public_int"); 299 static final VariableElement private_int = initVariableElement( 300 "com.example.fields.FieldsAccessModifiers", "private_int"); 301 static final VariableElement protected_float = initVariableElement( 302 "com.example.fields.FieldsAccessModifiers", "protected_float"); 303 static final VariableElement packagePrivate_long = initVariableElement( 304 "com.example.fields.FieldsAccessModifiers", "packagePrivate_long"); 305 } 306 } 307 initTypeElement(String name)308 protected static TypeElement initTypeElement(String name) { 309 var e = docletEnv.getElementUtils().getTypeElement(name); 310 assertNotNull(e); 311 return e; 312 } 313 initPackageElement(String name)314 private static PackageElement initPackageElement(String name) { 315 var e = docletEnv.getElementUtils().getPackageElement(name); 316 assertNotNull(e); 317 return e; 318 } 319 initMethodParam(String containingType, String methodSignature, String parameterName)320 private static VariableElement initMethodParam(String containingType, String methodSignature, 321 String parameterName) { 322 ExecutableElement method = initExecutableElement(containingType, methodSignature); 323 var params = method.getParameters() 324 .stream() 325 .filter(param -> param.getSimpleName().toString().equals(parameterName)) 326 .toList(); 327 328 assertEquals(1, params.size()); 329 return params.get(0); 330 } 331 initVariableElement(String containingType, String name)332 private static VariableElement initVariableElement(String containingType, String name) { 333 var t = initTypeElement(containingType); 334 var fields = t.getEnclosedElements() 335 .stream() 336 .filter(e -> e instanceof VariableElement) 337 .map(e -> (VariableElement) e) 338 .filter(ve -> name.equals(ve.getSimpleName().toString())) 339 .toList(); 340 341 assertEquals(1, fields.size()); 342 return fields.get(0); 343 } 344 345 /** 346 * Finds ExecutableElement in the environment by class type and element signature. Signature 347 * should be in the following format: {@code methodName(type1[,type2...])}, types are specified 348 * in a fully qualified form. For example: 349 * 350 * <ul> 351 * <li>{@code Constructor()}</li> 352 * <li>{@code Constructor(int)}</li> 353 * <li>{@code update(java.lang.String,float)}</li> 354 * </ul> 355 * 356 * @param type fully qualified class name 357 * @param signature signature of executable element 358 * @return ExecutableElement 359 */ initExecutableElement(String type, String signature)360 private static ExecutableElement initExecutableElement(String type, String signature) { 361 var t = initTypeElement(type); 362 var methods = t.getEnclosedElements() 363 .stream() 364 .filter(e -> e instanceof ExecutableElement) 365 .map(e -> (ExecutableElement) e) 366 .filter(exe -> signature.equals(exe.toString())) 367 .toList(); 368 369 assertEquals(1, methods.size()); 370 return methods.get(0); 371 } 372 } 373