1package org.robolectric; 2 3import static com.google.common.collect.ImmutableMap.of; 4import static java.nio.charset.StandardCharsets.UTF_8; 5import static org.assertj.core.api.Assertions.assertThat; 6import static org.robolectric.annotation.Config.DEFAULT_APPLICATION; 7 8import android.app.Application; 9import java.io.ByteArrayInputStream; 10import java.io.InputStream; 11import java.lang.reflect.Method; 12import java.util.HashMap; 13import java.util.Map; 14import org.junit.Ignore; 15import org.junit.Test; 16import org.junit.runner.RunWith; 17import org.junit.runners.JUnit4; 18import org.junit.runners.model.InitializationError; 19import org.robolectric.annotation.Config; 20import org.robolectric.shadows.ShadowView; 21import org.robolectric.shadows.ShadowViewGroup; 22 23@RunWith(JUnit4.class) 24public class ConfigMergerTest { 25 26 @Test public void defaultValuesAreMerged() throws Exception { 27 assertThat(configFor(Test2.class, "withoutAnnotation", 28 new Config.Builder().build()).manifest()) 29 .isEqualTo("AndroidManifest.xml"); 30 } 31 32 @Test public void globalValuesAreMerged() throws Exception { 33 assertThat(configFor(Test2.class, "withoutAnnotation", 34 new Config.Builder().setManifest("ManifestFromGlobal.xml").build()).manifest()) 35 .isEqualTo("ManifestFromGlobal.xml"); 36 } 37 38 @Test 39 public void whenClassHasConfigAnnotation_getConfig_shouldMergeClassAndMethodConfig() throws Exception { 40 assertConfig(configFor(Test1.class, "withoutAnnotation"), 41 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-test", "test/res", "test/assets", new Class[]{Test1.class}, new String[]{"com.example.test1"}, new String[]{"libs/test"}, BuildConfigConstants.class); 42 43 assertConfig(configFor(Test1.class, "withDefaultsAnnotation"), 44 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-test", "test/res", "test/assets", new Class[]{Test1.class}, new String[]{"com.example.test1"}, new String[]{"libs/test"}, BuildConfigConstants.class); 45 46 assertConfig(configFor(Test1.class, "withOverrideAnnotation"), 47 new int[] {9}, "furf", TestApplication.class, "com.example.method", "from-method", "method/res", "method/assets", new Class[]{Test1.class, Test2.class}, new String[]{"com.example.test1", "com.example.method1"}, new String[]{"libs/method", "libs/test"}, BuildConfigConstants2.class); 48 } 49 50 @Test 51 public void whenClassDoesntHaveConfigAnnotation_getConfig_shouldUseMethodConfig() throws Exception { 52 assertConfig(configFor(Test2.class, "withoutAnnotation"), 53 new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); 54 55 assertConfig(configFor(Test2.class, "withDefaultsAnnotation"), 56 new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); 57 58 assertConfig(configFor(Test2.class, "withOverrideAnnotation"), 59 new int[] {9}, "furf", TestFakeApp.class, "com.example.method", "from-method", "method/res", "method/assets", new Class[]{Test1.class}, new String[]{"com.example.method2"}, new String[]{"libs/method"}, BuildConfigConstants.class); 60 } 61 62 @Test 63 public void whenClassDoesntHaveConfigAnnotation_getConfig_shouldMergeParentClassAndMethodConfig() throws Exception { 64 assertConfig(configFor(Test5.class, "withoutAnnotation"), 65 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-test", "test/res", 66 "test/assets", new Class[]{Test1.class, Test1.class}, new String[]{"com.example.test1"}, 67 new String[]{"libs/test"}, BuildConfigConstants.class); 68 69 assertConfig(configFor(Test5.class, "withDefaultsAnnotation"), 70 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-test", "test/res", 71 "test/assets", new Class[]{Test1.class, Test1.class}, new String[]{"com.example.test1"}, 72 new String[]{"libs/test"}, BuildConfigConstants.class); 73 74 assertConfig(configFor(Test5.class, "withOverrideAnnotation"), 75 new int[] {14}, "foo", TestFakeApp.class, "com.example.test", "from-method5", "test/res", 76 "method5/assets", new Class[]{Test1.class, Test1.class, Test5.class}, 77 new String[]{"com.example.test1", "com.example.method5"}, new String[]{"libs/test"}, BuildConfigConstants5.class); 78 } 79 80 @Test 81 public void whenClassAndParentClassHaveConfigAnnotation_getConfig_shouldMergeParentClassAndMethodConfig() throws Exception { 82 assertConfig(configFor(Test6.class, "withoutAnnotation"), 83 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-class6", "class6/res", 84 "test/assets", new Class[]{Test1.class, Test1.class, Test6.class}, 85 new String[]{"com.example.test1", "com.example.test6"}, 86 new String[]{"libs/test"}, BuildConfigConstants6.class); 87 88 assertConfig(configFor(Test6.class, "withDefaultsAnnotation"), 89 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-class6", "class6/res", 90 "test/assets", new Class[]{Test1.class, Test1.class, Test6.class}, 91 new String[]{"com.example.test1", "com.example.test6"}, 92 new String[]{"libs/test"}, BuildConfigConstants6.class); 93 94 assertConfig(configFor(Test6.class, "withOverrideAnnotation"), 95 new int[] {14}, "foo", TestFakeApp.class, "com.example.test", "from-method5", "class6/res", 96 "method5/assets", new Class[]{Test1.class, Test1.class, Test6.class, Test5.class}, 97 new String[]{"com.example.test1", "com.example.method5", "com.example.test6"}, 98 new String[]{"libs/test"}, BuildConfigConstants5.class); 99 } 100 101 @Test 102 public void whenClassAndSubclassHaveConfigAnnotation_getConfig_shouldMergeClassSubclassAndMethodConfig() throws Exception { 103 assertConfig(configFor(Test3.class, "withoutAnnotation"), 104 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-subclass", "test/res", "test/assets", new Class[]{Test1.class}, new String[]{"com.example.test1"}, new String[]{"libs/test"}, BuildConfigConstants.class); 105 106 assertConfig(configFor(Test3.class, "withDefaultsAnnotation"), 107 new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-subclass", "test/res", "test/assets", new Class[]{Test1.class}, new String[]{"com.example.test1"}, new String[]{"libs/test"}, BuildConfigConstants.class); 108 109 assertConfig(configFor(Test3.class, "withOverrideAnnotation"), 110 new int[] {9},"furf", TestApplication.class, "com.example.method", "from-method", "method/res", "method/assets", new Class[]{Test1.class, Test2.class}, new String[]{"com.example.test1", "com.example.method1"}, new String[]{"libs/method", "libs/test"}, BuildConfigConstants2.class); 111 } 112 113 @Test 114 public void whenClassDoesntHaveConfigAnnotationButSubclassDoes_getConfig_shouldMergeSubclassAndMethodConfig() throws Exception { 115 assertConfig(configFor(Test4.class, "withoutAnnotation"), 116 new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "from-subclass", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); 117 118 assertConfig(configFor(Test4.class, "withDefaultsAnnotation"), 119 new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "from-subclass", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); 120 121 assertConfig(configFor(Test4.class, "withOverrideAnnotation"), 122 new int[] {9}, "furf", TestFakeApp.class, "com.example.method", "from-method", "method/res", "method/assets", new Class[]{Test1.class}, new String[]{"com.example.method2"}, new String[]{"libs/method"}, BuildConfigConstants.class); 123 } 124 125 @Test 126 public void shouldLoadDefaultsFromGlobalPropertiesFile() throws Exception { 127 String properties = "sdk: 432\n" + 128 "manifest: --none\n" + 129 "qualifiers: from-properties-file\n" + 130 "resourceDir: from/properties/file/res\n" + 131 "assetDir: from/properties/file/assets\n" + 132 "shadows: org.robolectric.shadows.ShadowView, org.robolectric.shadows.ShadowViewGroup\n" + 133 "application: org.robolectric.TestFakeApp\n" + 134 "packageName: com.example.test\n" + 135 "instrumentedPackages: com.example.test1, com.example.test2\n" + 136 "libraries: libs/test, libs/test2\n" + 137 "constants: " + ConfigMergerTest.BuildConfigConstants3.class.getName(); 138 139 assertConfig(configFor(Test2.class, "withoutAnnotation", of("robolectric.properties", properties)), 140 new int[] {432}, "--none", TestFakeApp.class, "com.example.test", "from-properties-file", "from/properties/file/res", "from/properties/file/assets", new Class[] {ShadowView.class, ShadowViewGroup.class}, new String[]{"com.example.test1", "com.example.test2"}, new String[]{"libs/test", "libs/test2"}, BuildConfigConstants3.class); 141 } 142 143 @Test 144 public void shouldMergeConfigFromTestClassPackageProperties() throws Exception { 145 assertConfig(configFor(Test2.class, "withoutAnnotation", of("org/robolectric/robolectric.properties", "sdk: 432\n")), 146 new int[] {432}, "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[] {}, new String[]{}, new String[]{}, null); 147 } 148 149 @Test 150 public void shouldMergeConfigUpPackageHierarchy() throws Exception { 151 assertConfig(configFor(Test2.class, "withoutAnnotation", 152 of( 153 "org/robolectric/robolectric.properties", "qualifiers: from-org-robolectric\nlibraries: FromOrgRobolectric\n", 154 "org/robolectric.properties", "sdk: 123\nqualifiers: from-org\nlibraries: FromOrg\n", 155 "robolectric.properties", "sdk: 456\nqualifiers: from-top-level\nlibraries: FromTopLevel\n" 156 ) 157 ), 158 new int[] {123}, "AndroidManifest.xml", DEFAULT_APPLICATION, "", "from-org-robolectric", "res", "assets", new Class[] {}, new String[]{}, 159 new String[]{"FromOrgRobolectric", "FromOrg", "FromTopLevel"}, null); 160 } 161 162 @Test 163 public void withEmptyShadowList_shouldLoadDefaultsFromGlobalPropertiesFile() throws Exception { 164 assertConfig(configFor(Test2.class, "withoutAnnotation", of("robolectric.properties", "shadows:")), 165 new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[] {}, new String[]{}, new String[]{}, null); 166 } 167 168 @Test public void testPackageHierarchyOf() throws Exception { 169 assertThat(new ConfigMerger().packageHierarchyOf(ConfigMergerTest.class)) 170 .containsExactly("org.robolectric", "org", ""); 171 } 172 173 ///////////////////////////// 174 175 private Config configFor(Class<?> testClass, String methodName, final Map<String, String> configProperties) throws InitializationError { 176 return configFor(testClass, methodName, configProperties, Config.Builder.defaults().build()); 177 } 178 179 private Config configFor(Class<?> testClass, String methodName) throws InitializationError { 180 Config.Implementation globalConfig = Config.Builder.defaults().build(); 181 return configFor(testClass, methodName, globalConfig); 182 } 183 184 private Config configFor(Class<?> testClass, String methodName, Config.Implementation globalConfig) throws InitializationError { 185 return configFor(testClass, methodName, new HashMap<>(), globalConfig); 186 } 187 188 private Config configFor(Class<?> testClass, String methodName, final Map<String, String> configProperties, Config.Implementation globalConfig) throws InitializationError { 189 Method info = getMethod(testClass, methodName); 190 return new ConfigMerger() { 191 @Override 192 InputStream getResourceAsStream(String resourceName) { 193 String properties = configProperties.get(resourceName); 194 return properties == null ? null : new ByteArrayInputStream(properties.getBytes(UTF_8)); 195 } 196 }.getConfig(testClass, info, globalConfig); 197 } 198 199 private static Method getMethod(Class<?> testClass, String methodName) { 200 try { 201 return testClass.getMethod(methodName); 202 } catch (NoSuchMethodException e) { 203 throw new RuntimeException(e); 204 } 205 } 206 207 private static void assertConfig(Config config, int[] sdk, String manifest, Class<? extends Application> application, String packageName, String qualifiers, String resourceDir, 208 String assetsDir, Class<?>[] shadows, String[] instrumentedPackages, String[] libraries, Class<?> constants) { 209 assertThat(config.sdk()).isEqualTo(sdk); 210 assertThat(config.manifest()).isEqualTo(manifest); 211 assertThat(config.application()).isEqualTo(application); 212 assertThat(config.packageName()).isEqualTo(packageName); 213 assertThat(config.qualifiers()).isEqualTo(qualifiers); 214 assertThat(config.resourceDir()).isEqualTo(resourceDir); 215 assertThat(config.assetDir()).isEqualTo(assetsDir); 216 assertThat(config.shadows()).containsExactly(shadows); 217 assertThat(config.instrumentedPackages()).containsExactlyInAnyOrder(instrumentedPackages); 218 assertThat(config.libraries()).containsExactlyInAnyOrder(libraries); 219 assertThat(config.constants()).isEqualTo(constants); 220 } 221 222 @Ignore 223 @Config(sdk = 1, manifest = "foo", application = TestFakeApp.class, packageName = "com.example.test", shadows = Test1.class, instrumentedPackages = "com.example.test1", libraries = "libs/test", qualifiers = "from-test", resourceDir = "test/res", assetDir = "test/assets", constants = BuildConfigConstants.class) 224 public static class Test1 { 225 @Test 226 public void withoutAnnotation() throws Exception { 227 } 228 229 @Test 230 @Config 231 public void withDefaultsAnnotation() throws Exception { 232 } 233 234 @Test 235 @Config(sdk = 9, manifest = "furf", application = TestApplication.class, packageName = "com.example.method", shadows = Test2.class, instrumentedPackages = "com.example.method1", libraries = "libs/method", qualifiers = "from-method", resourceDir = "method/res", assetDir = "method/assets", constants = BuildConfigConstants2.class) 236 public void withOverrideAnnotation() throws Exception { 237 } 238 } 239 240 @Ignore 241 public static class Test2 { 242 @Test 243 public void withoutAnnotation() throws Exception { 244 } 245 246 @Test 247 @Config 248 public void withDefaultsAnnotation() throws Exception { 249 } 250 251 @Test 252 @Config(sdk = 9, manifest = "furf", application = TestFakeApp.class, packageName = "com.example.method", shadows = Test1.class, instrumentedPackages = "com.example.method2", libraries = "libs/method", qualifiers = "from-method", resourceDir = "method/res", assetDir = "method/assets", constants = BuildConfigConstants.class) 253 public void withOverrideAnnotation() throws Exception { 254 } 255 } 256 257 @Ignore 258 @Config(qualifiers = "from-subclass") 259 public static class Test3 extends Test1 { 260 } 261 262 @Ignore 263 @Config(qualifiers = "from-subclass") 264 public static class Test4 extends Test2 { 265 } 266 267 @Ignore 268 public static class Test5 extends Test1 { 269 @Override 270 @Test 271 public void withoutAnnotation() throws Exception { 272 } 273 274 @Override 275 @Test 276 @Config 277 public void withDefaultsAnnotation() throws Exception { 278 } 279 280 @Override 281 @Test 282 @Config(sdk = 14, shadows = Test5.class, instrumentedPackages = "com.example.method5", packageName = "com.example.test", qualifiers = "from-method5", assetDir = "method5/assets", constants = BuildConfigConstants5.class) 283 public void withOverrideAnnotation() throws Exception { 284 } 285 } 286 287 public static class BuildConfigConstants {} 288 public static class BuildConfigConstants2 {} 289 public static class BuildConfigConstants3 {} 290 public static class BuildConfigConstants5 {} 291 public static class BuildConfigConstants6 {} 292 293 294 @Ignore 295 @Config(qualifiers = "from-class6", shadows = Test6.class, instrumentedPackages = "com.example.test6", resourceDir = "class6/res", constants = BuildConfigConstants6.class) 296 public static class Test6 extends Test5 { 297 } 298}