1 package org.robolectric; 2 3 import static com.google.common.collect.ImmutableMap.of; 4 import static com.google.common.truth.Truth.assertThat; 5 import static java.nio.charset.StandardCharsets.UTF_8; 6 import static org.robolectric.annotation.Config.DEFAULT_APPLICATION; 7 8 import android.app.Application; 9 import java.io.ByteArrayInputStream; 10 import java.io.InputStream; 11 import java.lang.reflect.Method; 12 import java.util.HashMap; 13 import java.util.Map; 14 import org.junit.Ignore; 15 import org.junit.Test; 16 import org.junit.runner.RunWith; 17 import org.junit.runners.JUnit4; 18 import org.junit.runners.model.InitializationError; 19 import org.robolectric.annotation.Config; 20 import org.robolectric.shadows.ShadowView; 21 import org.robolectric.shadows.ShadowViewGroup; 22 import org.robolectric.shadows.testing.TestApplication; 23 24 @RunWith(JUnit4.class) 25 public class ConfigMergerTest { 26 27 @Test public void defaultValuesAreMerged() throws Exception { 28 assertThat(configFor(Test2.class, "withoutAnnotation", 29 new Config.Builder().build()).manifest()) 30 .isEqualTo("AndroidManifest.xml"); 31 } 32 33 @Test public void globalValuesAreMerged() throws Exception { 34 assertThat(configFor(Test2.class, "withoutAnnotation", 35 new Config.Builder().setManifest("ManifestFromGlobal.xml").build()).manifest()) 36 .isEqualTo("ManifestFromGlobal.xml"); 37 } 38 39 @Test 40 public void whenClassHasConfigAnnotation_getConfig_shouldMergeClassAndMethodConfig() throws Exception { 41 assertConfig( 42 configFor(Test1.class, "withoutAnnotation"), 43 new int[] {1}, 44 "foo", 45 TestFakeApp.class, 46 "com.example.test", 47 "from-test", 48 "test/res", 49 "test/assets", 50 new Class[] {Test1.class}, 51 new String[] {"com.example.test1"}, 52 new String[] {"libs/test"}); 53 54 assertConfig( 55 configFor(Test1.class, "withDefaultsAnnotation"), 56 new int[] {1}, 57 "foo", 58 TestFakeApp.class, 59 "com.example.test", 60 "from-test", 61 "test/res", 62 "test/assets", 63 new Class[] {Test1.class}, 64 new String[] {"com.example.test1"}, 65 new String[] {"libs/test"}); 66 67 assertConfig( 68 configFor(Test1.class, "withOverrideAnnotation"), 69 new int[] {9}, 70 "furf", 71 TestApplication.class, 72 "com.example.method", 73 "from-method", 74 "method/res", 75 "method/assets", 76 new Class[] {Test1.class, Test2.class}, 77 new String[] {"com.example.test1", "com.example.method1"}, 78 new String[] {"libs/method", "libs/test"}); 79 } 80 81 @Test 82 public void whenClassDoesntHaveConfigAnnotation_getConfig_shouldUseMethodConfig() throws Exception { 83 assertConfig( 84 configFor(Test2.class, "withoutAnnotation"), 85 new int[0], 86 "AndroidManifest.xml", 87 DEFAULT_APPLICATION, 88 "", 89 "", 90 "res", 91 "assets", 92 new Class[] {}, 93 new String[] {}, 94 new String[] {}); 95 96 assertConfig( 97 configFor(Test2.class, "withDefaultsAnnotation"), 98 new int[0], 99 "AndroidManifest.xml", 100 DEFAULT_APPLICATION, 101 "", 102 "", 103 "res", 104 "assets", 105 new Class[] {}, 106 new String[] {}, 107 new String[] {}); 108 109 assertConfig( 110 configFor(Test2.class, "withOverrideAnnotation"), 111 new int[] {9}, 112 "furf", 113 TestFakeApp.class, 114 "com.example.method", 115 "from-method", 116 "method/res", 117 "method/assets", 118 new Class[] {Test1.class}, 119 new String[] {"com.example.method2"}, 120 new String[] {"libs/method"}); 121 } 122 123 @Test 124 public void whenClassDoesntHaveConfigAnnotation_getConfig_shouldMergeParentClassAndMethodConfig() throws Exception { 125 assertConfig( 126 configFor(Test5.class, "withoutAnnotation"), 127 new int[] {1}, 128 "foo", 129 TestFakeApp.class, 130 "com.example.test", 131 "from-test", 132 "test/res", 133 "test/assets", 134 new Class[] {Test1.class, Test1.class}, 135 new String[] {"com.example.test1"}, 136 new String[] {"libs/test"}); 137 138 assertConfig( 139 configFor(Test5.class, "withDefaultsAnnotation"), 140 new int[] {1}, 141 "foo", 142 TestFakeApp.class, 143 "com.example.test", 144 "from-test", 145 "test/res", 146 "test/assets", 147 new Class[] {Test1.class, Test1.class}, 148 new String[] {"com.example.test1"}, 149 new String[] {"libs/test"}); 150 151 assertConfig( 152 configFor(Test5.class, "withOverrideAnnotation"), 153 new int[] {14}, 154 "foo", 155 TestFakeApp.class, 156 "com.example.test", 157 "from-method5", 158 "test/res", 159 "method5/assets", 160 new Class[] {Test1.class, Test1.class, Test5.class}, 161 new String[] {"com.example.test1", "com.example.method5"}, 162 new String[] {"libs/test"}); 163 } 164 165 @Test 166 public void whenClassAndParentClassHaveConfigAnnotation_getConfig_shouldMergeParentClassAndMethodConfig() throws Exception { 167 assertConfig( 168 configFor(Test6.class, "withoutAnnotation"), 169 new int[] {1}, 170 "foo", 171 TestFakeApp.class, 172 "com.example.test", 173 "from-class6", 174 "class6/res", 175 "test/assets", 176 new Class[] {Test1.class, Test1.class, Test6.class}, 177 new String[] {"com.example.test1", "com.example.test6"}, 178 new String[] {"libs/test"}); 179 180 assertConfig( 181 configFor(Test6.class, "withDefaultsAnnotation"), 182 new int[] {1}, 183 "foo", 184 TestFakeApp.class, 185 "com.example.test", 186 "from-class6", 187 "class6/res", 188 "test/assets", 189 new Class[] {Test1.class, Test1.class, Test6.class}, 190 new String[] {"com.example.test1", "com.example.test6"}, 191 new String[] {"libs/test"}); 192 193 assertConfig( 194 configFor(Test6.class, "withOverrideAnnotation"), 195 new int[] {14}, 196 "foo", 197 TestFakeApp.class, 198 "com.example.test", 199 "from-method5", 200 "class6/res", 201 "method5/assets", 202 new Class[] {Test1.class, Test1.class, Test6.class, Test5.class}, 203 new String[] {"com.example.test1", "com.example.method5", "com.example.test6"}, 204 new String[] {"libs/test"}); 205 } 206 207 @Test 208 public void whenClassAndSubclassHaveConfigAnnotation_getConfig_shouldMergeClassSubclassAndMethodConfig() throws Exception { 209 assertConfig( 210 configFor(Test3.class, "withoutAnnotation"), 211 new int[] {1}, 212 "foo", 213 TestFakeApp.class, 214 "com.example.test", 215 "from-subclass", 216 "test/res", 217 "test/assets", 218 new Class[] {Test1.class}, 219 new String[] {"com.example.test1"}, 220 new String[] {"libs/test"}); 221 222 assertConfig( 223 configFor(Test3.class, "withDefaultsAnnotation"), 224 new int[] {1}, 225 "foo", 226 TestFakeApp.class, 227 "com.example.test", 228 "from-subclass", 229 "test/res", 230 "test/assets", 231 new Class[] {Test1.class}, 232 new String[] {"com.example.test1"}, 233 new String[] {"libs/test"}); 234 235 assertConfig( 236 configFor(Test3.class, "withOverrideAnnotation"), 237 new int[] {9}, 238 "furf", 239 TestApplication.class, 240 "com.example.method", 241 "from-method", 242 "method/res", 243 "method/assets", 244 new Class[] {Test1.class, Test2.class}, 245 new String[] {"com.example.test1", "com.example.method1"}, 246 new String[] {"libs/method", "libs/test"}); 247 } 248 249 @Test 250 public void whenClassDoesntHaveConfigAnnotationButSubclassDoes_getConfig_shouldMergeSubclassAndMethodConfig() throws Exception { 251 assertConfig( 252 configFor(Test4.class, "withoutAnnotation"), 253 new int[0], 254 "AndroidManifest.xml", 255 DEFAULT_APPLICATION, 256 "", 257 "from-subclass", 258 "res", 259 "assets", 260 new Class[] {}, 261 new String[] {}, 262 new String[] {}); 263 264 assertConfig( 265 configFor(Test4.class, "withDefaultsAnnotation"), 266 new int[0], 267 "AndroidManifest.xml", 268 DEFAULT_APPLICATION, 269 "", 270 "from-subclass", 271 "res", 272 "assets", 273 new Class[] {}, 274 new String[] {}, 275 new String[] {}); 276 277 assertConfig( 278 configFor(Test4.class, "withOverrideAnnotation"), 279 new int[] {9}, 280 "furf", 281 TestFakeApp.class, 282 "com.example.method", 283 "from-method", 284 "method/res", 285 "method/assets", 286 new Class[] {Test1.class}, 287 new String[] {"com.example.method2"}, 288 new String[] {"libs/method"}); 289 } 290 291 @Test 292 public void shouldLoadDefaultsFromGlobalPropertiesFile() throws Exception { 293 String properties = 294 "sdk: 432\n" 295 + "manifest: --none\n" 296 + "qualifiers: from-properties-file\n" 297 + "resourceDir: from/properties/file/res\n" 298 + "assetDir: from/properties/file/assets\n" 299 + "shadows: org.robolectric.shadows.ShadowView, org.robolectric.shadows.ShadowViewGroup\n" 300 + "application: org.robolectric.TestFakeApp\n" 301 + "packageName: com.example.test\n" 302 + "instrumentedPackages: com.example.test1, com.example.test2\n" 303 + "libraries: libs/test, libs/test2"; 304 305 assertConfig( 306 configFor(Test2.class, "withoutAnnotation", of("robolectric.properties", properties)), 307 new int[] {432}, 308 "--none", 309 TestFakeApp.class, 310 "com.example.test", 311 "from-properties-file", 312 "from/properties/file/res", 313 "from/properties/file/assets", 314 new Class[] {ShadowView.class, ShadowViewGroup.class}, 315 new String[] {"com.example.test1", "com.example.test2"}, 316 new String[] {"libs/test", "libs/test2"}); 317 } 318 319 @Test 320 public void shouldMergeConfigFromTestClassPackageProperties() throws Exception { 321 assertConfig( 322 configFor( 323 Test2.class, 324 "withoutAnnotation", 325 of("org/robolectric/robolectric.properties", "sdk: 432\n")), 326 new int[] {432}, 327 "AndroidManifest.xml", 328 DEFAULT_APPLICATION, 329 "", 330 "", 331 "res", 332 "assets", 333 new Class[] {}, 334 new String[] {}, 335 new String[] {}); 336 } 337 338 @Test 339 public void shouldMergeConfigUpPackageHierarchy() throws Exception { 340 assertConfig( 341 configFor( 342 Test2.class, 343 "withoutAnnotation", 344 of( 345 "org/robolectric/robolectric.properties", 346 "qualifiers: from-org-robolectric\nlibraries: FromOrgRobolectric\n", 347 "org/robolectric.properties", 348 "sdk: 123\nqualifiers: from-org\nlibraries: FromOrg\n", 349 "robolectric.properties", 350 "sdk: 456\nqualifiers: from-top-level\nlibraries: FromTopLevel\n")), 351 new int[] {123}, 352 "AndroidManifest.xml", 353 DEFAULT_APPLICATION, 354 "", 355 "from-org-robolectric", 356 "res", 357 "assets", 358 new Class[] {}, 359 new String[] {}, 360 new String[] {"FromOrgRobolectric", "FromOrg", "FromTopLevel"}); 361 } 362 363 @Test 364 public void withEmptyShadowList_shouldLoadDefaultsFromGlobalPropertiesFile() throws Exception { 365 assertConfig( 366 configFor(Test2.class, "withoutAnnotation", of("robolectric.properties", "shadows:")), 367 new int[0], 368 "AndroidManifest.xml", 369 DEFAULT_APPLICATION, 370 "", 371 "", 372 "res", 373 "assets", 374 new Class[] {}, 375 new String[] {}, 376 new String[] {}); 377 } 378 379 @Test public void testPackageHierarchyOf() throws Exception { 380 assertThat(new ConfigMerger().packageHierarchyOf(ConfigMergerTest.class)) 381 .containsExactly("org.robolectric", "org", ""); 382 } 383 384 ///////////////////////////// 385 386 private Config configFor(Class<?> testClass, String methodName, final Map<String, String> configProperties) throws InitializationError { 387 return configFor(testClass, methodName, configProperties, Config.Builder.defaults().build()); 388 } 389 390 private Config configFor(Class<?> testClass, String methodName) throws InitializationError { 391 Config.Implementation globalConfig = Config.Builder.defaults().build(); 392 return configFor(testClass, methodName, globalConfig); 393 } 394 395 private Config configFor(Class<?> testClass, String methodName, Config.Implementation globalConfig) throws InitializationError { 396 return configFor(testClass, methodName, new HashMap<>(), globalConfig); 397 } 398 399 private Config configFor(Class<?> testClass, String methodName, final Map<String, String> configProperties, Config.Implementation globalConfig) throws InitializationError { 400 Method info = getMethod(testClass, methodName); 401 return new ConfigMerger() { 402 @Override 403 InputStream getResourceAsStream(String resourceName) { 404 String properties = configProperties.get(resourceName); 405 return properties == null ? null : new ByteArrayInputStream(properties.getBytes(UTF_8)); 406 } 407 }.getConfig(testClass, info, globalConfig); 408 } 409 410 private static Method getMethod(Class<?> testClass, String methodName) { 411 try { 412 return testClass.getMethod(methodName); 413 } catch (NoSuchMethodException e) { 414 throw new RuntimeException(e); 415 } 416 } 417 418 private static void assertConfig( 419 Config config, 420 int[] sdk, 421 String manifest, 422 Class<? extends Application> application, 423 String packageName, 424 String qualifiers, 425 String resourceDir, 426 String assetsDir, 427 Class<?>[] shadows, 428 String[] instrumentedPackages, 429 String[] libraries) { 430 assertThat(config.sdk()).isEqualTo(sdk); 431 assertThat(config.manifest()).isEqualTo(manifest); 432 assertThat(config.application()).isEqualTo(application); 433 assertThat(config.packageName()).isEqualTo(packageName); 434 assertThat(config.qualifiers()).isEqualTo(qualifiers); 435 assertThat(config.resourceDir()).isEqualTo(resourceDir); 436 assertThat(config.assetDir()).isEqualTo(assetsDir); 437 assertThat(config.shadows()).asList().containsAllIn(shadows).inOrder(); 438 assertThat(config.instrumentedPackages()).asList().containsAllIn(instrumentedPackages); 439 assertThat(config.libraries()).asList().containsAllIn(libraries); 440 } 441 442 @Ignore 443 @Config( 444 sdk = 1, 445 manifest = "foo", 446 application = TestFakeApp.class, 447 packageName = "com.example.test", 448 shadows = Test1.class, 449 instrumentedPackages = "com.example.test1", 450 libraries = "libs/test", 451 qualifiers = "from-test", 452 resourceDir = "test/res", 453 assetDir = "test/assets") 454 public static class Test1 { 455 @Test 456 public void withoutAnnotation() throws Exception { 457 } 458 459 @Test 460 @Config 461 public void withDefaultsAnnotation() throws Exception { 462 } 463 464 @Test 465 @Config( 466 sdk = 9, 467 manifest = "furf", 468 application = TestApplication.class, 469 packageName = "com.example.method", 470 shadows = Test2.class, 471 instrumentedPackages = "com.example.method1", 472 libraries = "libs/method", 473 qualifiers = "from-method", 474 resourceDir = "method/res", 475 assetDir = "method/assets") 476 public void withOverrideAnnotation() throws Exception {} 477 } 478 479 @Ignore 480 public static class Test2 { 481 @Test 482 public void withoutAnnotation() throws Exception { 483 } 484 485 @Test 486 @Config 487 public void withDefaultsAnnotation() throws Exception { 488 } 489 490 @Test 491 @Config( 492 sdk = 9, 493 manifest = "furf", 494 application = TestFakeApp.class, 495 packageName = "com.example.method", 496 shadows = Test1.class, 497 instrumentedPackages = "com.example.method2", 498 libraries = "libs/method", 499 qualifiers = "from-method", 500 resourceDir = "method/res", 501 assetDir = "method/assets") 502 public void withOverrideAnnotation() throws Exception {} 503 } 504 505 @Ignore 506 @Config(qualifiers = "from-subclass") 507 public static class Test3 extends Test1 { 508 } 509 510 @Ignore 511 @Config(qualifiers = "from-subclass") 512 public static class Test4 extends Test2 { 513 } 514 515 @Ignore 516 public static class Test5 extends Test1 { 517 @Override 518 @Test 519 public void withoutAnnotation() throws Exception { 520 } 521 522 @Override 523 @Test 524 @Config 525 public void withDefaultsAnnotation() throws Exception { 526 } 527 528 @Override 529 @Test 530 @Config( 531 sdk = 14, 532 shadows = Test5.class, 533 instrumentedPackages = "com.example.method5", 534 packageName = "com.example.test", 535 qualifiers = "from-method5", 536 assetDir = "method5/assets") 537 public void withOverrideAnnotation() throws Exception {} 538 } 539 540 @Ignore 541 @Config( 542 qualifiers = "from-class6", 543 shadows = Test6.class, 544 instrumentedPackages = "com.example.test6", 545 resourceDir = "class6/res") 546 public static class Test6 extends Test5 {} 547 } 548