1 /* <lambda>null2 * Copyright (C) 2020 The Dagger Authors. 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 dagger.hilt.android.processor.internal.viewmodel 18 19 import androidx.room.compiler.processing.ExperimentalProcessingApi 20 import androidx.room.compiler.processing.util.Source 21 import com.google.common.collect.ImmutableList 22 import dagger.hilt.android.testing.compile.HiltCompilerTests 23 import dagger.internal.codegen.ComponentProcessor 24 import dagger.internal.codegen.KspComponentProcessor 25 import org.junit.Test 26 import org.junit.runner.RunWith 27 import org.junit.runners.JUnit4 28 29 @ExperimentalProcessingApi 30 @RunWith(JUnit4::class) 31 class ViewModelValidationPluginTest { 32 33 private fun testCompiler(vararg sources: Source): HiltCompilerTests.HiltCompiler = 34 HiltCompilerTests.hiltCompiler(ImmutableList.copyOf(sources)) 35 .withAdditionalJavacProcessors( 36 ComponentProcessor.withTestPlugins(ViewModelValidationPlugin()), 37 ViewModelProcessor() 38 ) 39 .withAdditionalKspProcessors( 40 KspComponentProcessor.Provider.withTestPlugins(ViewModelValidationPlugin()), 41 KspViewModelProcessor.Provider() 42 ) 43 44 private val hiltAndroidApp = 45 """ 46 package test; 47 48 import android.app.Application; 49 import dagger.hilt.android.HiltAndroidApp; 50 51 @HiltAndroidApp(Application.class) 52 public class TestApplication extends Hilt_TestApplication {} 53 """ 54 .toJFO("test.TestApplication") 55 56 @Test 57 fun injectViewModelIsProhibited() { 58 val hiltActivity = 59 """ 60 package test; 61 62 import androidx.fragment.app.FragmentActivity; 63 import dagger.hilt.android.AndroidEntryPoint; 64 import javax.inject.Inject; 65 66 @AndroidEntryPoint(FragmentActivity.class) 67 public class TestActivity extends Hilt_TestActivity { 68 @Inject Foo foo; 69 } 70 """ 71 .toJFO("test.TestActivity") 72 val hiltViewModel = 73 """ 74 package test; 75 76 import androidx.lifecycle.ViewModel; 77 import dagger.hilt.android.lifecycle.HiltViewModel; 78 import javax.inject.Inject; 79 80 @HiltViewModel 81 class MyViewModel extends ViewModel { 82 @Inject MyViewModel() { } 83 } 84 """ 85 .toJFO("test.MyViewModel") 86 val foo = 87 """ 88 package test; 89 90 import javax.inject.Inject; 91 92 final class Foo { 93 @Inject Foo(MyViewModel viewModel) {} 94 } 95 """ 96 .toJFO("test.Foo") 97 98 testCompiler(foo, hiltViewModel, hiltAndroidApp, hiltActivity).compile() { subject -> 99 subject.compilationDidFail() 100 subject.hasErrorCount(1) 101 subject.hasErrorContaining("Injection of an @HiltViewModel class is prohibited") 102 } 103 } 104 105 @Test 106 fun fieldInjectedViewModelIsProhibited() { 107 val hiltActivity = 108 """ 109 package test; 110 111 import androidx.fragment.app.FragmentActivity; 112 import dagger.hilt.android.AndroidEntryPoint; 113 import javax.inject.Inject; 114 115 @AndroidEntryPoint(FragmentActivity.class) 116 public class TestActivity extends Hilt_TestActivity { 117 @Inject MyViewModel viewModel; 118 } 119 """ 120 .toJFO("test.TestActivity") 121 val hiltViewModel = 122 """ 123 package test; 124 125 import androidx.lifecycle.ViewModel; 126 import dagger.hilt.android.lifecycle.HiltViewModel; 127 import javax.inject.Inject; 128 129 @HiltViewModel 130 class MyViewModel extends ViewModel { 131 @Inject MyViewModel() { } 132 } 133 """ 134 .toJFO("test.MyViewModel") 135 136 testCompiler(hiltViewModel, hiltAndroidApp, hiltActivity).compile() { subject -> 137 subject.compilationDidFail() 138 subject.hasErrorCount(1) 139 subject.hasErrorContaining("Injection of an @HiltViewModel class is prohibited") 140 } 141 } 142 143 @Test 144 fun injectViewModelFromViewModelComponentIsProhibited() { 145 // Use an @HiltViewModel that injects a Foo to get the binding inside the ViewModelComponent 146 val hiltViewModel = 147 """ 148 package test; 149 150 import androidx.lifecycle.ViewModel; 151 import dagger.hilt.android.lifecycle.HiltViewModel; 152 import javax.inject.Inject; 153 154 @HiltViewModel 155 class MyViewModel extends ViewModel { 156 @Inject MyViewModel(Foo foo) { } 157 } 158 """ 159 .toJFO("test.MyViewModel") 160 161 val foo = 162 """ 163 package test; 164 165 import javax.inject.Inject; 166 import javax.inject.Provider; 167 168 final class Foo { 169 @Inject Foo(Provider<MyViewModel> viewModelProvider) {} 170 } 171 """ 172 .toJFO("test.Foo") 173 174 testCompiler(foo, hiltViewModel, hiltAndroidApp).compile() { subject -> 175 subject.compilationDidFail() 176 subject.hasErrorCount(1) 177 subject.hasErrorContaining("Injection of an @HiltViewModel class is prohibited") 178 } 179 } 180 181 @Test 182 fun injectOverriddenViewModelBindingIsAllowed() { 183 val hiltActivity = 184 """ 185 package test; 186 187 import androidx.fragment.app.FragmentActivity; 188 import dagger.hilt.android.AndroidEntryPoint; 189 import javax.inject.Inject; 190 191 @AndroidEntryPoint(FragmentActivity.class) 192 public class TestActivity extends Hilt_TestActivity { 193 @Inject Foo foo; 194 } 195 """ 196 .toJFO("test.TestActivity") 197 val hiltViewModel = 198 """ 199 package test; 200 201 import androidx.lifecycle.ViewModel; 202 import dagger.hilt.android.lifecycle.HiltViewModel; 203 import javax.inject.Inject; 204 205 @HiltViewModel 206 class MyViewModel extends ViewModel { 207 @Inject MyViewModel() { } 208 } 209 """ 210 .toJFO("test.MyViewModel") 211 val foo = 212 """ 213 package test; 214 215 import javax.inject.Inject; 216 217 final class Foo { 218 @Inject Foo(MyViewModel viewModel) {} 219 } 220 """ 221 .toJFO("test.Foo") 222 val activityModule = 223 """ 224 package test; 225 226 import dagger.Module; 227 import dagger.Provides; 228 import dagger.hilt.InstallIn; 229 import dagger.hilt.android.components.ActivityComponent; 230 231 @InstallIn(ActivityComponent.class) 232 @Module 233 public final class ActivityModule { 234 @Provides static MyViewModel provideMyViewModel() { 235 // Normally you'd expect this to use a ViewModelProvider or something but 236 // since this test is just testing the binding graph, for simplicity just return 237 // null. 238 return null; 239 } 240 } 241 """ 242 .toJFO("test.ActivityModule") 243 244 testCompiler(foo, activityModule, hiltViewModel, hiltAndroidApp, hiltActivity).compile() { 245 subject -> 246 subject.hasErrorCount(0) 247 } 248 } 249 250 @Test 251 fun injectQualifiedViewModelBindingIsAllowed() { 252 val hiltActivity = 253 """ 254 package test; 255 256 import androidx.fragment.app.FragmentActivity; 257 import dagger.hilt.android.AndroidEntryPoint; 258 import javax.inject.Inject; 259 260 @AndroidEntryPoint(FragmentActivity.class) 261 public class TestActivity extends Hilt_TestActivity { 262 @Inject Foo foo; 263 } 264 """ 265 .toJFO("test.TestActivity") 266 val hiltViewModel = 267 """ 268 package test; 269 270 import androidx.lifecycle.ViewModel; 271 import dagger.hilt.android.lifecycle.HiltViewModel; 272 import javax.inject.Inject; 273 274 @HiltViewModel 275 class MyViewModel extends ViewModel { 276 @Inject MyViewModel() { } 277 } 278 """ 279 .toJFO("test.MyViewModel") 280 val foo = 281 """ 282 package test; 283 284 import javax.inject.Inject; 285 286 final class Foo { 287 @Inject Foo(@ActivityModule.MyQualifier MyViewModel viewModel) {} 288 } 289 """ 290 .toJFO("test.Foo") 291 val activityModule = 292 """ 293 package test; 294 295 import dagger.Module; 296 import dagger.Provides; 297 import dagger.hilt.InstallIn; 298 import dagger.hilt.android.components.ActivityComponent; 299 import javax.inject.Qualifier; 300 301 @InstallIn(ActivityComponent.class) 302 @Module 303 public final class ActivityModule { 304 @Qualifier 305 public @interface MyQualifier {} 306 307 @Provides 308 @MyQualifier 309 static MyViewModel provideMyViewModel() { 310 // Normally you'd expect this to use a ViewModelProvider or something but 311 // since this test is just testing the binding graph, for simplicity just return 312 // null. 313 return null; 314 } 315 } 316 """ 317 .toJFO("test.ActivityModule") 318 319 testCompiler(foo, activityModule, hiltViewModel, hiltAndroidApp, hiltActivity).compile() { 320 subject -> 321 subject.hasErrorCount(0) 322 } 323 } 324 325 // Regression test for not handling array types properly 326 @Test 327 fun correctlyAllowsOtherBindings() { 328 val hiltActivity = 329 """ 330 package test; 331 332 import androidx.fragment.app.FragmentActivity; 333 import dagger.hilt.android.AndroidEntryPoint; 334 import javax.inject.Inject; 335 336 @AndroidEntryPoint(FragmentActivity.class) 337 public class TestActivity extends Hilt_TestActivity { 338 @Inject Foo foo; 339 } 340 """ 341 .toJFO("test.TestActivity") 342 val hiltViewModel = 343 """ 344 package test; 345 346 import androidx.lifecycle.ViewModel; 347 import dagger.hilt.android.lifecycle.HiltViewModel; 348 import javax.inject.Inject; 349 350 @HiltViewModel 351 class MyViewModel extends ViewModel { 352 @Inject MyViewModel() { } 353 } 354 """ 355 .toJFO("test.MyViewModel") 356 val foo = 357 """ 358 package test; 359 360 import javax.inject.Inject; 361 362 final class Foo { 363 @Inject Foo(Long[] longArray) {} 364 } 365 """ 366 .toJFO("test.Foo") 367 val activityModule = 368 """ 369 package test; 370 371 import dagger.Module; 372 import dagger.Provides; 373 import dagger.hilt.InstallIn; 374 import dagger.hilt.android.components.ActivityComponent; 375 376 @InstallIn(ActivityComponent.class) 377 @Module 378 public final class ActivityModule { 379 @Provides 380 static Long[] provideLongArray() { 381 return null; 382 } 383 } 384 """ 385 .toJFO("test.ActivityModule") 386 387 testCompiler(foo, activityModule, hiltViewModel, hiltAndroidApp, hiltActivity).compile() { 388 subject -> 389 subject.hasErrorCount(0) 390 } 391 } 392 } 393