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.photopicker.lint.test 18 19 import com.android.photopicker.lint.LazyInjectionDetector 20 import com.android.tools.lint.checks.infrastructure.LintDetectorTest 21 import com.android.tools.lint.checks.infrastructure.TestFile 22 import com.android.tools.lint.checks.infrastructure.TestLintTask 23 import com.android.tools.lint.detector.api.Detector 24 import com.android.tools.lint.detector.api.Issue 25 import org.junit.Test 26 import org.junit.runner.RunWith 27 import org.junit.runners.JUnit4 28 29 @Suppress("UnstableApiUsage") 30 @RunWith(JUnit4::class) 31 class LazyInjectionDetectorTest : LintDetectorTest() { getDetectornull32 override fun getDetector(): Detector = LazyInjectionDetector() 33 34 override fun getIssues(): List<Issue> = listOf(LazyInjectionDetector.ISSUE) 35 36 override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) 37 38 private val entryPointStub: TestFile = 39 java( 40 """ 41 package dagger.hilt; 42 import static java.lang.annotation.RetentionPolicy.CLASS; 43 import java.lang.annotation.ElementType; 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.Target; 46 47 @Retention(CLASS) public @interface EntryPoint {} 48 """ 49 ) 50 51 private val inject: TestFile = 52 kotlin( 53 """ 54 package javax.inject.Inject 55 56 @Retention(AnnotationRetention.RUNTIME) annotation class Inject 57 """ 58 ) 59 .indented() 60 61 private val configurationManager: TestFile = 62 kotlin( 63 """ 64 package com.android.photopicker.core.configuration 65 66 class ConfigurationManager {} 67 """ 68 ) 69 .indented() 70 71 private val dataService: TestFile = 72 kotlin( 73 """ 74 package com.android.photopicker.data.DataService 75 76 class DataService {} 77 """ 78 ) 79 .indented() 80 81 private val stubs = arrayOf(configurationManager, inject, dataService, entryPointStub) 82 83 @Test 84 fun testInjectConfigurationManager() { 85 lint() 86 .files( 87 kotlin( 88 """ 89 package com.android.photopicker 90 91 import com.android.photopicker.core.configuration.ConfigurationManager 92 import dagger.Lazy 93 import javax.inject.Inject 94 95 class MainActivity { 96 97 @Inject lateinit var configurationManager: ConfigurationManager 98 99 } 100 """ 101 ) 102 .indented(), 103 *stubs 104 ) 105 .run() 106 .expectClean() 107 } 108 109 @Test testInjectDataServiceLazilynull110 fun testInjectDataServiceLazily() { 111 lint() 112 .files( 113 kotlin( 114 """ 115 package com.android.photopicker 116 117 import com.android.photopicker.core.configuration.ConfigurationManager 118 import com.android.photopicker.data.DataService 119 import dagger.Lazy 120 import javax.inject.Inject 121 122 class MainActivity { 123 124 @Inject lateinit var configurationManager: ConfigurationManager 125 @Inject lateinit var dataService: Lazy<DataService> 126 127 } 128 """ 129 ) 130 .indented(), 131 *stubs 132 ) 133 .run() 134 .expectClean() 135 } 136 137 @Test testInjectDataServiceNotLazynull138 fun testInjectDataServiceNotLazy() { 139 lint() 140 .files( 141 kotlin( 142 """ 143 package com.android.photopicker 144 145 import com.android.photopicker.core.configuration.ConfigurationManager 146 import com.android.photopicker.data.DataService 147 import dagger.Lazy 148 import javax.inject.Inject 149 150 class MainActivity { 151 152 @Inject lateinit var configurationManager: ConfigurationManager 153 @Inject lateinit var dataService: DataService 154 155 } 156 """ 157 ) 158 .indented(), 159 *stubs 160 ) 161 .run() 162 .expectContains(LazyInjectionDetector.INVALID_INJECTION_FIELD_ERROR) 163 } 164 165 @Test testInjectDataServiceNotLazyNotEnforcedClassnull166 fun testInjectDataServiceNotLazyNotEnforcedClass() { 167 lint() 168 .files( 169 kotlin( 170 """ 171 package com.android.photopicker 172 173 import com.android.photopicker.core.configuration.ConfigurationManager 174 import com.android.photopicker.data.DataService 175 import dagger.Lazy 176 import javax.inject.Inject 177 178 class SomeFeatureViewModel { 179 180 @Inject lateinit var configurationManager: ConfigurationManager 181 @Inject lateinit var dataService: DataService 182 183 } 184 """ 185 ) 186 .indented(), 187 *stubs 188 ) 189 .run() 190 .expectClean() 191 } 192 193 @Test testInjectDataServiceNotLazyEntryPointnull194 fun testInjectDataServiceNotLazyEntryPoint() { 195 lint() 196 .files( 197 kotlin( 198 """ 199 package com.android.photopicker.core.embedded 200 201 import com.android.photopicker.core.configuration.ConfigurationManager 202 import com.android.photopicker.data.DataService 203 import dagger.Lazy 204 import dagger.hilt.EntryPoint 205 import javax.inject.Inject 206 207 class Session { 208 209 @EntryPoint 210 @InstallIn(EmbeddedServiceComponent::class) 211 interface EmbeddedEntryPoint { 212 fun configurationManager(): Lazy<ConfigurationManager> 213 fun dataService(): DataService 214 } 215 216 } 217 """ 218 ) 219 .indented(), 220 *stubs 221 ) 222 .run() 223 .expectContains(LazyInjectionDetector.INVALID_INJECTION_FIELD_ERROR) 224 } 225 226 @Test testInjectDataServiceNotLazyNotEntryPointnull227 fun testInjectDataServiceNotLazyNotEntryPoint() { 228 lint() 229 .files( 230 kotlin( 231 """ 232 package com.android.photopicker.core.embedded 233 234 import com.android.photopicker.core.configuration.ConfigurationManager 235 import com.android.photopicker.data.DataService 236 import javax.inject.Inject 237 238 class Session { 239 240 @InstallIn(EmbeddedServiceComponent::class) 241 interface EmbeddedEntryPoint { 242 fun configurationManager(): ConfigurationManager 243 fun dataService(): DataService 244 } 245 246 } 247 """ 248 ) 249 .indented(), 250 *stubs 251 ) 252 .run() 253 .expectClean() 254 } 255 256 @Test testInjectDataServiceLazyEntryPointnull257 fun testInjectDataServiceLazyEntryPoint() { 258 lint() 259 .files( 260 kotlin( 261 """ 262 package com.android.photopicker.core.embedded 263 264 import com.android.photopicker.core.configuration.ConfigurationManager 265 import com.android.photopicker.data.DataService 266 import dagger.Lazy 267 import dagger.hilt.EntryPoint 268 import javax.inject.Inject 269 270 class Session { 271 272 @EntryPoint 273 @InstallIn(EmbeddedServiceComponent::class) 274 interface EmbeddedEntryPoint { 275 fun configurationManager(): ConfigurationManager 276 fun dataService(): Lazy<DataService> 277 } 278 279 } 280 """ 281 ) 282 .indented(), 283 *stubs 284 ) 285 .run() 286 .expectClean() 287 } 288 } 289