package org.robolectric; import android.app.Application; import org.junit.Ignore; import org.junit.Test; import org.junit.runners.model.InitializationError; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowView; import org.robolectric.shadows.ShadowViewGroup; import org.robolectric.util.ReflectionHelpers; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Map; import static com.google.common.collect.ImmutableMap.of; import static org.assertj.core.api.Assertions.assertThat; import static org.robolectric.annotation.Config.DEFAULT_APPLICATION; import static org.robolectric.util.TestUtil.stringify; public class ConfigMergerTest { @Test public void defaultValuesAreMerged() throws Exception { assertThat(configFor(Test2.class, "withoutAnnotation", new Config.Builder().build()).manifest()) .isEqualTo("AndroidManifest.xml"); } @Test public void globalValuesAreMerged() throws Exception { assertThat(configFor(Test2.class, "withoutAnnotation", new Config.Builder().setManifest("ManifestFromGlobal.xml").build()).manifest()) .isEqualTo("ManifestFromGlobal.xml"); } @Test public void whenClassHasConfigAnnotation_getConfig_shouldMergeClassAndMethodConfig() throws Exception { assertConfig(configFor(Test1.class, "withoutAnnotation"), 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); assertConfig(configFor(Test1.class, "withDefaultsAnnotation"), 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); assertConfig(configFor(Test1.class, "withOverrideAnnotation"), 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); } @Test public void whenClassDoesntHaveConfigAnnotation_getConfig_shouldUseMethodConfig() throws Exception { assertConfig(configFor(Test2.class, "withoutAnnotation"), new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); assertConfig(configFor(Test2.class, "withDefaultsAnnotation"), new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); assertConfig(configFor(Test2.class, "withOverrideAnnotation"), 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); } @Test public void whenClassDoesntHaveConfigAnnotation_getConfig_shouldMergeParentClassAndMethodConfig() throws Exception { assertConfig(configFor(Test5.class, "withoutAnnotation"), 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); assertConfig(configFor(Test5.class, "withDefaultsAnnotation"), 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); assertConfig(configFor(Test5.class, "withOverrideAnnotation"), new int[] {14}, "foo", TestFakeApp.class, "com.example.test", "from-method5", "test/res", "method5/assets", new Class[]{Test1.class, Test5.class}, new String[]{"com.example.test1", "com.example.method5"}, new String[]{"libs/test"}, BuildConfigConstants5.class); } @Test public void whenClassAndParentClassHaveConfigAnnotation_getConfig_shouldMergeParentClassAndMethodConfig() throws Exception { assertConfig(configFor(Test6.class, "withoutAnnotation"), new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-class6", "class6/res", "test/assets", new Class[]{Test1.class, Test6.class}, new String[]{"com.example.test1", "com.example.test6"}, new String[]{"libs/test"}, BuildConfigConstants6.class); assertConfig(configFor(Test6.class, "withDefaultsAnnotation"), new int[] {1}, "foo", TestFakeApp.class, "com.example.test", "from-class6", "class6/res", "test/assets", new Class[]{Test1.class, Test6.class}, new String[]{"com.example.test1", "com.example.test6"}, new String[]{"libs/test"}, BuildConfigConstants6.class); assertConfig(configFor(Test6.class, "withOverrideAnnotation"), new int[] {14}, "foo", TestFakeApp.class, "com.example.test", "from-method5", "class6/res", "method5/assets", new Class[]{Test1.class, Test5.class, Test6.class}, new String[]{"com.example.test1", "com.example.method5", "com.example.test6"}, new String[]{"libs/test"}, BuildConfigConstants5.class); } @Test public void whenClassAndSubclassHaveConfigAnnotation_getConfig_shouldMergeClassSubclassAndMethodConfig() throws Exception { assertConfig(configFor(Test3.class, "withoutAnnotation"), 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); assertConfig(configFor(Test3.class, "withDefaultsAnnotation"), 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); assertConfig(configFor(Test3.class, "withOverrideAnnotation"), 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); } @Test public void whenClassDoesntHaveConfigAnnotationButSubclassDoes_getConfig_shouldMergeSubclassAndMethodConfig() throws Exception { assertConfig(configFor(Test4.class, "withoutAnnotation"), new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "from-subclass", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); assertConfig(configFor(Test4.class, "withDefaultsAnnotation"), new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "from-subclass", "res", "assets", new Class[]{}, new String[]{}, new String[]{}, Void.class); assertConfig(configFor(Test4.class, "withOverrideAnnotation"), 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); } @Test public void shouldLoadDefaultsFromGlobalPropertiesFile() throws Exception { String properties = "sdk: 432\n" + "manifest: --none\n" + "qualifiers: from-properties-file\n" + "resourceDir: from/properties/file/res\n" + "assetDir: from/properties/file/assets\n" + "shadows: org.robolectric.shadows.ShadowView, org.robolectric.shadows.ShadowViewGroup\n" + "application: org.robolectric.TestFakeApp\n" + "packageName: com.example.test\n" + "instrumentedPackages: com.example.test1, com.example.test2\n" + "libraries: libs/test, libs/test2\n" + "constants: " + ConfigMergerTest.BuildConfigConstants3.class.getName(); assertConfig(configFor(Test2.class, "withoutAnnotation", of("robolectric.properties", properties)), 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); } @Test public void shouldMergeConfigFromTestClassPackageProperties() throws Exception { assertConfig(configFor(Test2.class, "withoutAnnotation", of("org/robolectric/robolectric.properties", "sdk: 432\n")), new int[] {432}, "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[] {}, new String[]{}, new String[]{}, null); } @Test public void shouldMergeConfigUpPackageHierarchy() throws Exception { assertConfig(configFor(Test2.class, "withoutAnnotation", of( "org/robolectric/robolectric.properties", "qualifiers: from-org-robolectric\nlibraries: FromOrgRobolectric\n", "org/robolectric.properties", "sdk: 123\nqualifiers: from-org\nlibraries: FromOrg\n", "robolectric.properties", "sdk: 456\nqualifiers: from-top-level\nlibraries: FromTopLevel\n" ) ), new int[] {123}, "AndroidManifest.xml", DEFAULT_APPLICATION, "", "from-org-robolectric", "res", "assets", new Class[] {}, new String[]{}, new String[]{"FromOrgRobolectric", "FromOrg", "FromTopLevel"}, null); } @Test public void withEmptyShadowList_shouldLoadDefaultsFromGlobalPropertiesFile() throws Exception { assertConfig(configFor(Test2.class, "withoutAnnotation", of("robolectric.properties", "shadows:")), new int[0], "AndroidManifest.xml", DEFAULT_APPLICATION, "", "", "res", "assets", new Class[] {}, new String[]{}, new String[]{}, null); } @Test public void testPackageHierarchyOf() throws Exception { assertThat(new ConfigMerger().packageHierarchyOf(ConfigMergerTest.class)) .containsExactly("org.robolectric", "org", ""); } ///////////////////////////// private Config configFor(Class<?> testClass, String methodName, final Map<String, String> configProperties) throws InitializationError { Method info = getMethod(testClass, methodName); return new ConfigMerger() { @Override InputStream getResourceAsStream(String resourceName) { String properties = configProperties.get(resourceName); return properties == null ? null : new ByteArrayInputStream(properties.getBytes()); } }.getConfig(testClass, info, Config.Builder.defaults().build()); } private Config configFor(Class<?> testClass, String methodName) { Config.Implementation globalConfig = Config.Builder.defaults().build(); return configFor(testClass, methodName, globalConfig); } private Config configFor(Class<?> testClass, String methodName, Config.Implementation globalConfig) { Method info = getMethod(testClass, methodName); return new ConfigMerger().getConfig(testClass, info, globalConfig); } private static Method getMethod(Class<?> testClass, String methodName) { try { return testClass.getMethod(methodName); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } private void assertConfig(Config config, int[] sdk, String manifest, Class<? extends Application> application, String packageName, String qualifiers, String resourceDir, String assetsDir, Class<?>[] shadows, String[] instrumentedPackages, String[] libraries, Class<?> constants) { assertThat(stringify(config)).isEqualTo(stringify(sdk, manifest, application, packageName, qualifiers, resourceDir, assetsDir, shadows, instrumentedPackages, libraries, constants)); } @Ignore @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) public static class Test1 { @Test public void withoutAnnotation() throws Exception { } @Test @Config public void withDefaultsAnnotation() throws Exception { } @Test @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) public void withOverrideAnnotation() throws Exception { } } @Ignore public static class Test2 { @Test public void withoutAnnotation() throws Exception { } @Test @Config public void withDefaultsAnnotation() throws Exception { } @Test @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) public void withOverrideAnnotation() throws Exception { } } @Ignore @Config(qualifiers = "from-subclass") public static class Test3 extends Test1 { } @Ignore @Config(qualifiers = "from-subclass") public static class Test4 extends Test2 { } @Ignore public static class Test5 extends Test1 { @Override @Test public void withoutAnnotation() throws Exception { } @Override @Test @Config public void withDefaultsAnnotation() throws Exception { } @Override @Test @Config(sdk = 14, shadows = Test5.class, instrumentedPackages = "com.example.method5", packageName = "com.example.test", qualifiers = "from-method5", assetDir = "method5/assets", constants = BuildConfigConstants5.class) public void withOverrideAnnotation() throws Exception { } } public static class BuildConfigConstants {} public static class BuildConfigConstants2 {} public static class BuildConfigConstants3 {} public static class BuildConfigConstants5 {} public static class BuildConfigConstants6 {} @Ignore @Config(qualifiers = "from-class6", shadows = Test6.class, instrumentedPackages = "com.example.test6", resourceDir = "class6/res", constants = BuildConfigConstants6.class) public static class Test6 extends Test5 { } }