1 /* 2 * Copyright (C) 2024 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.testsuite.codebase 18 19 import com.android.tools.metalava.model.ClassItem 20 import com.android.tools.metalava.model.testsuite.BaseModelTest 21 import com.android.tools.metalava.testing.java 22 import com.android.tools.metalava.testing.kotlin 23 import kotlin.test.assertNotNull 24 import kotlin.test.assertNull 25 import kotlin.test.assertSame 26 import org.junit.Test 27 import org.junit.runners.Parameterized 28 29 class ParameterizedFindClassTest : BaseModelTest() { 30 31 @Parameterized.Parameter(0) lateinit var params: TestParams 32 33 data class TestParams( 34 val className: String, 35 val expectedFound: Boolean, 36 /** 37 * This is only tested when `true` as even though the class might be unknown some models 38 * that work with partial information, e.g. text, will fabricate an instance just in case it 39 * was real. 40 */ 41 val expectedResolved: Boolean = expectedFound, 42 ) { toStringnull43 override fun toString(): String { 44 return className 45 } 46 } 47 48 companion object { 49 private val params = 50 listOf( 51 TestParams( 52 className = "test.pkg.Foo", 53 expectedFound = true, 54 ), 55 TestParams( 56 className = "test.pkg.Unknown", 57 expectedFound = false, 58 ), 59 TestParams( 60 // Test to make sure that a class whose name does not match the file name will 61 // be found. 62 className = "test.pkg.SecondInFile", 63 expectedFound = true, 64 ), 65 // The following classes will be explicitly loaded. Although these are used 66 // implicitly the behavior differs between models so is hard to test. By specifying 67 // them explicitly it makes the tests more consistent. 68 TestParams( 69 className = "java.lang.Object", 70 expectedFound = true, 71 ), 72 TestParams( 73 className = "java.lang.Throwable", 74 expectedFound = true, 75 ), 76 // The following classes are implicitly used, directly, or indirectly and are tested 77 // to check that the implicit use does not accidentally include them when they 78 // should not. However, they should all be resolvable. 79 TestParams( 80 className = "java.lang.annotation.Annotation", 81 expectedFound = false, 82 expectedResolved = true, 83 ), 84 TestParams( 85 className = "java.lang.Enum", 86 expectedFound = false, 87 expectedResolved = true, 88 ), 89 TestParams( 90 className = "java.lang.Comparable", 91 expectedFound = false, 92 expectedResolved = true, 93 ), 94 // The following should not be used implicitly by anything. 95 TestParams( 96 className = "java.io.File", 97 expectedFound = false, 98 expectedResolved = true, 99 ), 100 ) 101 paramsnull102 @JvmStatic @Parameterized.Parameters fun params() = params 103 } 104 105 private fun assertFound(className: String, expectedFound: Boolean, foundClass: ClassItem?) { 106 if (expectedFound) { 107 assertNotNull(foundClass, message = "$className should exist") 108 } else { 109 assertNull(foundClass, message = "$className should not exist") 110 } 111 } 112 113 @Test test findClass()null114 fun `test findClass()`() { 115 runCodebaseTest( 116 signature( 117 """ 118 // Signature format: 2.0 119 package test.pkg { 120 public class Foo { 121 method public Object foo(Throwable) throws Throwable; 122 } 123 public class SecondInFile { 124 } 125 } 126 """ 127 ), 128 java( 129 """ 130 package test.pkg; 131 public class Foo { 132 private Foo() {} 133 public Object foo(Throwable t) throws Throwable {throw new Throwable();} 134 } 135 public class SecondInFile { 136 } 137 """ 138 ), 139 kotlin( 140 """ 141 package test.pkg 142 class Foo 143 private constructor() { 144 @Throws(Throwable::class) 145 fun foo(t: Throwable): Any {throw Throwable()} 146 } 147 class SecondInFile { 148 } 149 """ 150 ), 151 ) { 152 val fooMethod = codebase.assertClass("test.pkg.Foo").methods().single() 153 154 // Force loading of the Object classes by resolving the return type which is 155 // java.lang.Object. 156 fooMethod.returnType().asClass() 157 158 // Force loading of the Throwable classes by resolving the parameter's type which is 159 // java.lang.Object. 160 fooMethod.parameters().single().type().asClass() 161 162 val className = params.className 163 val foundClass = codebase.findClass(className) 164 assertFound(className, params.expectedFound, foundClass) 165 166 val resolvedClass = codebase.resolveClass(className) 167 if (foundClass == null) { 168 // If the class was not found then resolving might have found it. 169 if (params.expectedResolved) { 170 assertNotNull(resolvedClass, message = "expected to resolve $className") 171 } 172 173 // If the class was resolved then it must now be found. 174 if (resolvedClass != null) { 175 val foundClassAfterResolving = codebase.findClass(className) 176 assertSame( 177 resolvedClass, 178 foundClassAfterResolving, 179 message = "could not find $className even though it was previously resolved" 180 ) 181 } 182 } else { 183 // If the class was found then it must be resolved to the same class. 184 assertSame( 185 foundClass, 186 resolvedClass, 187 message = "could not resolve $className even though it was previously found" 188 ) 189 } 190 } 191 } 192 } 193