blob: 2e48b211ecbeb60879056a8524cf8cc101ab2baa [file] [log] [blame]
package android.content.res;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.graphics.Color;
import android.util.TypedValue;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.internal.DoNotInstrument;
import org.robolectric.testapp.R;
/**
* Compatibility test for {@link Resources.Theme}
*/
@DoNotInstrument
@RunWith(AndroidJUnit4.class)
public class ThemeTest {
private Resources resources;
private Context context;
@Before
public void setup() throws Exception {
context = InstrumentationRegistry.getTargetContext();
resources = context.getResources();
}
@Test
public void withEmptyTheme_returnsEmptyAttributes() throws Exception {
assertThat(resources.newTheme().obtainStyledAttributes(new int[] {R.attr.string1}).hasValue(0))
.isFalse();
}
@Test
public void shouldLookUpStylesFromStyleResId() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedArray a = theme.obtainStyledAttributes(R.style.MyCustomView, R.styleable.CustomView);
boolean enabled = a.getBoolean(R.styleable.CustomView_aspectRatioEnabled, false);
assertThat(enabled).isTrue();
}
@Test
public void shouldApplyStylesFromResourceReference() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedArray a =
theme.obtainStyledAttributes(null, R.styleable.CustomView, R.attr.animalStyle, 0);
int animalStyleId = a.getResourceId(R.styleable.CustomView_animalStyle, 0);
assertThat(animalStyleId).isEqualTo(R.style.Gastropod);
assertThat(a.getFloat(R.styleable.CustomView_aspectRatio, 0.2f)).isEqualTo(1.69f);
}
@Test
public void shouldApplyStylesFromAttributeReference() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_ThirdTheme, true);
TypedArray a =
theme.obtainStyledAttributes(null, R.styleable.CustomView, R.attr.animalStyle, 0);
int animalStyleId = a.getResourceId(R.styleable.CustomView_animalStyle, 0);
assertThat(animalStyleId).isEqualTo(R.style.Gastropod);
assertThat(a.getFloat(R.styleable.CustomView_aspectRatio, 0.2f)).isEqualTo(1.69f);
}
@Test
public void shouldGetValuesFromAttributeReference() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_ThirdTheme, true);
TypedValue value1 = new TypedValue();
TypedValue value2 = new TypedValue();
boolean resolved1 = theme.resolveAttribute(R.attr.someLayoutOne, value1, true);
boolean resolved2 = theme.resolveAttribute(R.attr.someLayoutTwo, value2, true);
assertThat(resolved1).isTrue();
assertThat(resolved2).isTrue();
assertThat(value1.resourceId).isEqualTo(R.layout.activity_main);
assertThat(value2.resourceId).isEqualTo(R.layout.activity_main);
assertThat(value1.coerceToString()).isEqualTo(value2.coerceToString());
}
@Test
public void withResolveRefsFalse_shouldResolveValue() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedValue value = new TypedValue();
boolean resolved = theme.resolveAttribute(R.attr.logoWidth, value, false);
assertThat(resolved).isTrue();
assertThat(value.type).isEqualTo(TypedValue.TYPE_REFERENCE);
assertThat(value.data).isEqualTo(R.dimen.test_dp_dimen);
}
@Test
public void withResolveRefsFalse_shouldNotResolveResource() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedValue value = new TypedValue();
boolean resolved = theme.resolveAttribute(R.attr.logoHeight, value, false);
assertThat(resolved).isTrue();
assertThat(value.type).isEqualTo(TypedValue.TYPE_REFERENCE);
assertThat(value.data).isEqualTo(R.dimen.test_dp_dimen);
}
@Test
public void withResolveRefsTrue_shouldResolveResource() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedValue value = new TypedValue();
boolean resolved = theme.resolveAttribute(R.attr.logoHeight, value, true);
assertThat(resolved).isTrue();
assertThat(value.type).isEqualTo(TypedValue.TYPE_DIMENSION);
assertThat(value.resourceId).isEqualTo(R.dimen.test_dp_dimen);
assertThat(value.coerceToString()).isEqualTo("8.0dip");
}
@Test
public void failToResolveCircularReference() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedValue value = new TypedValue();
boolean resolved = theme.resolveAttribute(R.attr.isSugary, value, false);
assertThat(resolved).isFalse();
}
@Test
public void canResolveAttrReferenceToDifferentPackage() throws Exception {
Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
TypedValue value = new TypedValue();
boolean resolved = theme.resolveAttribute(R.attr.styleReference, value, false);
assertThat(resolved).isTrue();
assertThat(value.type).isEqualTo(TypedValue.TYPE_REFERENCE);
assertThat(value.data).isEqualTo(R.style.Widget_AnotherTheme_Button);
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void forStylesWithImplicitParents_shouldInheritValuesNotDefinedInChild() throws Exception {
Resources.Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_Robolectric_ImplicitChild, true);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string3}).getString(0))
.isEqualTo("string 3 from Theme.Robolectric.ImplicitChild");
}
@Test
public void whenAThemeHasExplicitlyEmptyParentAttr_shouldHaveNoParent() throws Exception {
Resources.Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_Robolectric_EmptyParent, true);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).hasValue(0)).isFalse();
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void shouldApplyParentStylesFromAttrs() throws Exception {
Resources.Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_AnotherTheme, true);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.AnotherTheme");
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string3}).getString(0))
.isEqualTo("string 3 from Theme.Robolectric");
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void setTo_shouldCopyAllAttributesToEmptyTheme() throws Exception {
Resources.Theme theme1 = resources.newTheme();
theme1.applyStyle(R.style.Theme_Robolectric, false);
assertThat(theme1.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
Resources.Theme theme2 = resources.newTheme();
theme2.setTo(theme1);
assertThat(theme2.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void setTo_whenDestThemeIsModified_sourceThemeShouldNotMutate() throws Exception {
Resources.Theme sourceTheme = resources.newTheme();
sourceTheme.applyStyle(R.style.Theme_Robolectric, false);
assertThat(sourceTheme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
Resources.Theme destTheme = resources.newTheme();
destTheme.setTo(sourceTheme);
destTheme.applyStyle(R.style.Theme_AnotherTheme, true);
assertThat(sourceTheme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void setTo_whenSourceThemeIsModified_destThemeShouldNotMutate() throws Exception {
Resources.Theme sourceTheme = resources.newTheme();
sourceTheme.applyStyle(R.style.Theme_Robolectric, false);
assertThat(sourceTheme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
Resources.Theme destTheme = resources.newTheme();
destTheme.setTo(sourceTheme);
sourceTheme.applyStyle(R.style.Theme_AnotherTheme, true);
assertThat(destTheme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void applyStyle_withForceFalse_shouldApplyButNotOverwriteExistingAttributeValues()
throws Exception {
Resources.Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_Robolectric, false);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
theme.applyStyle(R.style.Theme_AnotherTheme, false);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string2}).getString(0))
.isEqualTo("string 2 from Theme.AnotherTheme");
}
@Test
@Ignore("todo: incorrect behavior on robolectric vs framework?")
public void applyStyle_withForceTrue_shouldApplyAndOverwriteExistingAttributeValues()
throws Exception {
Resources.Theme theme = resources.newTheme();
theme.applyStyle(R.style.Theme_Robolectric, false);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
theme.applyStyle(R.style.Theme_AnotherTheme, true);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.AnotherTheme");
// force apply the original theme; values should be overwritten
theme.applyStyle(R.style.Theme_Robolectric, true);
assertThat(theme.obtainStyledAttributes(new int[] {R.attr.string1}).getString(0))
.isEqualTo("string 1 from Theme.Robolectric");
}
@Test
public void shouldFindInheritedAndroidAttributeInTheme() throws Exception {
context.setTheme(R.style.Theme_AnotherTheme);
Resources.Theme theme1 = context.getTheme();
TypedArray typedArray =
theme1.obtainStyledAttributes(new int[] {R.attr.typeface, android.R.attr.buttonStyle});
assertThat(typedArray.hasValue(0)).isTrue(); // animalStyle
assertThat(typedArray.hasValue(1)).isTrue(); // layout_height
}
@Test
public void themesShouldBeApplyableAcrossResources() throws Exception {
Resources.Theme themeFromSystem = Resources.getSystem().newTheme();
themeFromSystem.applyStyle(android.R.style.Theme_Light, true);
Resources.Theme themeFromApp = resources.newTheme();
themeFromApp.applyStyle(android.R.style.Theme, true);
// themeFromSystem is Theme_Light, which has a white background...
assertThat(
themeFromSystem
.obtainStyledAttributes(new int[] {android.R.attr.colorBackground})
.getColor(0, 123))
.isEqualTo(Color.WHITE);
// themeFromApp is Theme, which has a black background...
assertThat(
themeFromApp
.obtainStyledAttributes(new int[] {android.R.attr.colorBackground})
.getColor(0, 123))
.isEqualTo(Color.BLACK);
themeFromApp.setTo(themeFromSystem);
// themeFromApp now has style values from themeFromSystem, so now it has a black background...
assertThat(
themeFromApp
.obtainStyledAttributes(new int[] {android.R.attr.colorBackground})
.getColor(0, 123))
.isEqualTo(Color.WHITE);
}
@Test
public void styleResolutionShouldIgnoreThemes() throws Exception {
Resources.Theme themeFromSystem = resources.newTheme();
themeFromSystem.applyStyle(android.R.style.Theme_DeviceDefault, true);
themeFromSystem.applyStyle(R.style.ThemeWithSelfReferencingTextAttr, true);
assertThat(
themeFromSystem
.obtainStyledAttributes(new int[] {android.R.attr.textAppearance})
.getResourceId(0, 0))
.isEqualTo(0);
}
}