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 18 19 import com.android.tools.lint.checks.infrastructure.TestFiles.base64gzip 20 import com.android.tools.lint.checks.infrastructure.TestFiles.jar 21 import com.android.tools.lint.checks.infrastructure.TestFiles.xml 22 import org.junit.Assert.assertEquals 23 import org.junit.Assert.assertFalse 24 import org.junit.Assert.assertThrows 25 import org.junit.Assert.assertTrue 26 import org.junit.Test 27 import java.io.File 28 import java.lang.reflect.Modifier 29 import java.net.URLClassLoader 30 import kotlin.text.Charsets.UTF_8 31 32 class RewriteAnnotationsTest : DriverTest() { 33 @Test Test copying private annotations from one of the stubsnull34 fun `Test copying private annotations from one of the stubs`() { 35 val source = File("stub-annotations") 36 assertTrue(source.path, source.isDirectory) 37 val target = temporaryFolder.newFolder() 38 runDriver( 39 ARG_NO_COLOR, 40 ARG_NO_BANNER, 41 42 ARG_COPY_ANNOTATIONS, 43 source.path, 44 target.path, 45 46 ARG_CLASS_PATH, 47 getAndroidJar().path 48 ) 49 // Source retention explicitly listed: Shouldn't exist 50 val nullable = File(target, "android/annotation/SdkConstant.java") 51 assertFalse("${nullable.path} exists", nullable.isFile) 52 53 // Source retention androidx: Shouldn't exist 54 val nonNull = File(target, "androidx/annotation/NonNull.java") 55 assertFalse("${nonNull.path} exists", nonNull.isFile) 56 57 // Class retention: Should be converted 58 59 val recentlyNull = File(target, "androidx/annotation/RecentlyNullable.java") 60 assertTrue("${recentlyNull.path} doesn't exist", recentlyNull.isFile) 61 assertEquals( 62 """ 63 /* 64 * Copyright (C) 2018 The Android Open Source Project 65 * 66 * Licensed under the Apache License, Version 2.0 (the "License"); 67 * you may not use this file except in compliance with the License. 68 * You may obtain a copy of the License at 69 * 70 * http://www.apache.org/licenses/LICENSE-2.0 71 * 72 * Unless required by applicable law or agreed to in writing, software 73 * distributed under the License is distributed on an "AS IS" BASIS, 74 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 75 * See the License for the specific language governing permissions and 76 * limitations under the License. 77 */ 78 package androidx.annotation; 79 80 import static java.lang.annotation.ElementType.FIELD; 81 import static java.lang.annotation.ElementType.METHOD; 82 import static java.lang.annotation.ElementType.PARAMETER; 83 import static java.lang.annotation.ElementType.TYPE_USE; 84 import static java.lang.annotation.RetentionPolicy.CLASS; 85 86 import java.lang.annotation.Retention; 87 import java.lang.annotation.Target; 88 89 /** Stub only annotation. Do not use directly. */ 90 @Retention(CLASS) 91 @Target({METHOD, PARAMETER, FIELD}) 92 @interface RecentlyNullable {} 93 """.trimIndent().trim(), 94 recentlyNull.readText(UTF_8).trim().replace("\r\n", "\n") 95 ) 96 } 97 98 @Test Test stub-annotations containing unknown annotationnull99 fun `Test stub-annotations containing unknown annotation`() { 100 val source = temporaryFolder.newFolder() 101 File("stub-annotations").copyRecursively(source) 102 assertTrue(source.path, source.isDirectory) 103 val target = temporaryFolder.newFolder() 104 105 val fooSource = 106 """ 107 package android.annotation; 108 109 import static java.lang.annotation.ElementType.FIELD; 110 import static java.lang.annotation.ElementType.METHOD; 111 import static java.lang.annotation.ElementType.PARAMETER; 112 import static java.lang.annotation.RetentionPolicy.SOURCE; 113 114 import java.lang.annotation.Retention; 115 import java.lang.annotation.Target; 116 117 /** Stub only annotation. Do not use directly. */ 118 @Retention(SOURCE) 119 @Target({METHOD, PARAMETER, FIELD}) 120 public @interface Foo {} 121 """ 122 123 File(source, "src/main/java/android/annotation/Unknown.java").writeText(fooSource) 124 assertThrows(IllegalStateException::class.java) { 125 runDriver( 126 ARG_NO_COLOR, 127 ARG_NO_BANNER, 128 129 ARG_COPY_ANNOTATIONS, 130 source.path, 131 target.path, 132 133 ARG_CLASS_PATH, 134 getAndroidJar().path 135 ) 136 } 137 } 138 139 @Test Test rewriting the bytecode for one of the public annotationsnull140 fun `Test rewriting the bytecode for one of the public annotations`() { 141 val bytecode = base64gzip( 142 "androidx/annotation/CallSuper.class", 143 "" + 144 "H4sIAAAAAAAAAIWPsU4CQRRF70NhEQWxJMZoLCjdxs6KIMYCA2E3NlbD8kKG" + 145 "DDNkmSXwaxZ+gB9FfGMBFps4yczc5J53kve9//wC8IirCK0IlxHahEbiijzj" + 146 "F22Y0OorY5JixfnDQm0UoTMprNdLftdrPTXcs9Z55bWza8LdMDCxUXYeq0MR" + 147 "T9izDemJUN0oU4i3+w86dkZnuzDQH/aShHBTPpCqfM5euPvyfmB4KcZ0t2KB" + 148 "am+D9HX0LDZlZ7nTs+1f9rAqoX2UjaYLzjzhttR/3L9LIFTkniCcCk5/3ypq" + 149 "8l9LiqSrM87QwHmIHyDGBZo/ObYRQoUBAAA=" 150 ) 151 152 val compiledStubs = temporaryFolder.newFolder("compiled-stubs") 153 bytecode.createFile(compiledStubs) 154 155 runDriver( 156 ARG_NO_COLOR, 157 ARG_NO_BANNER, 158 159 ARG_REWRITE_ANNOTATIONS, 160 compiledStubs.path, 161 162 ARG_CLASS_PATH, 163 getAndroidJar().path 164 ) 165 166 // Load the class to make sure it's legit 167 val url = compiledStubs.toURI().toURL() 168 val loader = URLClassLoader(arrayOf(url), null) 169 val annotationClass = loader.loadClass("androidx.annotation.CallSuper") 170 val modifiers = annotationClass.modifiers 171 assertEquals(0, modifiers and Modifier.PUBLIC) 172 assertTrue(annotationClass.isAnnotation) 173 } 174 175 @Test Test rewriting the bytecode for one of the public annotations in a jar filenull176 fun `Test rewriting the bytecode for one of the public annotations in a jar file`() { 177 val bytecode = base64gzip( 178 "androidx/annotation/CallSuper.class", 179 "" + 180 "H4sIAAAAAAAAAIWPsU4CQRRF70NhEQWxJMZoLCjdxs6KIMYCA2E3NlbD8kKG" + 181 "DDNkmSXwaxZ+gB9FfGMBFps4yczc5J53kve9//wC8IirCK0IlxHahEbiijzj" + 182 "F22Y0OorY5JixfnDQm0UoTMprNdLftdrPTXcs9Z55bWza8LdMDCxUXYeq0MR" + 183 "T9izDemJUN0oU4i3+w86dkZnuzDQH/aShHBTPpCqfM5euPvyfmB4KcZ0t2KB" + 184 "am+D9HX0LDZlZ7nTs+1f9rAqoX2UjaYLzjzhttR/3L9LIFTkniCcCk5/3ypq" + 185 "8l9LiqSrM87QwHmIHyDGBZo/ObYRQoUBAAA=" 186 ) 187 188 val jarDesc = jar( 189 "myjar.jar", 190 bytecode, 191 xml("foo/bar/baz.xml", "<hello-world/>") 192 ) 193 194 val jarFile = jarDesc.createFile(temporaryFolder.root) 195 196 runDriver( 197 ARG_NO_COLOR, 198 ARG_NO_BANNER, 199 200 ARG_REWRITE_ANNOTATIONS, 201 jarFile.path, 202 203 ARG_CLASS_PATH, 204 getAndroidJar().path 205 ) 206 207 // Load the class to make sure it's legit 208 val url = jarFile.toURI().toURL() 209 val loader = URLClassLoader(arrayOf(url), null) 210 val annotationClass = loader.loadClass("androidx.annotation.CallSuper") 211 val modifiers = annotationClass.modifiers 212 assertEquals(0, modifiers and Modifier.PUBLIC) 213 assertTrue(annotationClass.isAnnotation) 214 } 215 } 216