1 package org.robolectric.res;
2 
3 import static com.google.common.truth.Truth.assertThat;
4 import static org.junit.Assert.assertEquals;
5 import static org.junit.Assert.fail;
6 import static org.mockito.Mockito.mock;
7 import static org.mockito.Mockito.when;
8 
9 import com.google.common.base.Strings;
10 import javax.annotation.Nonnull;
11 import org.junit.Before;
12 import org.junit.Test;
13 import org.junit.runner.RunWith;
14 import org.junit.runners.JUnit4;
15 import org.robolectric.res.android.ConfigDescription;
16 import org.robolectric.res.android.ResTable_config;
17 
18 @RunWith(JUnit4.class)
19 public class ResBundleTest {
20   private ResBundle.ResMap resMap = new ResBundle.ResMap();
21   private ResName resName;
22 
23   @Before
setUp()24   public void setUp() throws Exception {
25     resName = new ResName("a:b/c");
26   }
27 
28   @Test
closestMatchIsPicked()29   public void closestMatchIsPicked() {
30     TypedResource<String> val1 = createStringTypedResource("v16");
31     resMap.put(resName, val1);
32     TypedResource<String> val2 = createStringTypedResource("v17");
33     resMap.put(resName, val2);
34 
35     TypedResource v = resMap.pick(resName, from("v18"));
36     assertThat(v).isEqualTo(val2);
37   }
38 
39   @Test
firstValIsPickedWhenNoMatch()40   public void firstValIsPickedWhenNoMatch() {
41     TypedResource<String> val1 = createStringTypedResource("en");
42     resMap.put(resName, val1);
43     TypedResource<String> val2 = createStringTypedResource("fr");
44     resMap.put(resName, val2);
45 
46     TypedResource v = resMap.pick(resName, from("en-v18"));
47     assertThat(v).isEqualTo(val1);
48   }
49 
50   @Test
bestValIsPickedForSdkVersion()51   public void bestValIsPickedForSdkVersion() {
52     TypedResource<String> val1 = createStringTypedResource("v16");
53     resMap.put(resName, val1);
54     TypedResource<String> val2 = createStringTypedResource("v17");
55     resMap.put(resName, val2);
56 
57     TypedResource v = resMap.pick(resName, from("v26"));
58     assertThat(v).isEqualTo(val2);
59   }
60 
61   @Test
eliminatedValuesAreNotPickedForVersion()62   public void eliminatedValuesAreNotPickedForVersion() {
63     TypedResource<String> val1 = createStringTypedResource("land-v16");
64     resMap.put(resName, val1);
65     TypedResource<String> val2 = createStringTypedResource("v17");
66     resMap.put(resName, val2);
67 
68     TypedResource v = resMap.pick(resName, from("land-v18"));
69     assertThat(v).isEqualTo(val1);
70   }
71 
72   @Test
greaterVersionsAreNotPicked()73   public void greaterVersionsAreNotPicked() {
74     TypedResource<String> val1 = createStringTypedResource("v11");
75     resMap.put(resName, val1);
76     TypedResource<String> val2 = createStringTypedResource("v19");
77     resMap.put(resName, val2);
78 
79     TypedResource v = resMap.pick(resName, from("v18"));
80     assertThat(v).isEqualTo(val1);
81   }
82 
83   @Test
greaterVersionsAreNotPickedReordered()84   public void greaterVersionsAreNotPickedReordered() {
85     TypedResource<String> val1 = createStringTypedResource("v19");
86     resMap.put(resName, val1);
87     TypedResource<String> val2 = createStringTypedResource("v11");
88     resMap.put(resName, val2);
89 
90     TypedResource v = resMap.pick(resName, from("v18"));
91     assertThat(v).isEqualTo(val2);
92   }
93 
94   @Test
greaterVersionsAreNotPickedMoreQualifiers()95   public void greaterVersionsAreNotPickedMoreQualifiers() {
96     // List the contradicting qualifier first, in case the algorithm has a tendency
97     // to pick the first qualifier when none of the qualifiers are a "perfect" match.
98     TypedResource<String> val1 = createStringTypedResource("anydpi-v21");
99     resMap.put(resName, val1);
100     TypedResource<String> val2 = createStringTypedResource("xhdpi-v9");
101     resMap.put(resName, val2);
102 
103     TypedResource v = resMap.pick(resName, from("v18"));
104     assertThat(v).isEqualTo(val2);
105   }
106 
107   @Test
onlyMatchingVersionsQualifiersWillBePicked()108   public void onlyMatchingVersionsQualifiersWillBePicked() {
109     TypedResource<String> val1 = createStringTypedResource("v16");
110     resMap.put(resName, val1);
111     TypedResource<String> val2 = createStringTypedResource("sw600dp-v17");
112     resMap.put(resName, val2);
113 
114     TypedResource v = resMap.pick(resName, from("v18"));
115     assertThat(v).isEqualTo(val1);
116   }
117 
118   @Test
illegalResourceQualifierThrowsException()119   public void illegalResourceQualifierThrowsException() {
120     TypedResource<String> val1 = createStringTypedResource("en-v12");
121     resMap.put(resName, val1);
122 
123     try {
124       resMap.pick(resName, from("nosuchqualifier"));
125       fail("Expected exception to be caught");
126     } catch (IllegalArgumentException e) {
127       assertThat(e).hasMessageThat().startsWith("Invalid qualifiers \"nosuchqualifier\"");
128     }
129   }
130 
131   @Test
shouldMatchQualifiersPerAndroidSpec()132   public void shouldMatchQualifiersPerAndroidSpec() throws Exception {
133     assertEquals("en-port", asResMap(
134         "",
135         "en",
136         "fr-rCA",
137         "en-port",
138         "en-notouch-12key",
139         "port-ldpi",
140         "land-notouch-12key").pick(resName,
141         from("en-rGB-port-hdpi-notouch-12key-v25")).asString());
142   }
143 
144   @Test
shouldMatchQualifiersInSizeRange()145   public void shouldMatchQualifiersInSizeRange() throws Exception {
146     assertEquals("sw300dp-port", asResMap(
147         "",
148         "sw200dp",
149         "sw350dp-port",
150         "sw300dp-port",
151         "sw300dp").pick(resName,
152         from("sw320dp-port-v25")).asString());
153   }
154 
155   @Test
shouldPreferWidthOverHeight()156   public void shouldPreferWidthOverHeight() throws Exception {
157     assertEquals("sw300dp-w200dp", asResMap(
158         "",
159         "sw200dp",
160         "sw200dp-w300dp",
161         "sw300dp-w200dp",
162         "w300dp").pick(resName,
163         from("sw320dp-w320dp-v25")).asString());
164   }
165 
166   @Test
shouldNotOverwriteValuesWithMatchingQualifiers()167   public void shouldNotOverwriteValuesWithMatchingQualifiers() {
168     ResBundle bundle = new ResBundle();
169     XmlContext xmlContext = mock(XmlContext.class);
170     when(xmlContext.getQualifiers()).thenReturn(Qualifiers.parse("--"));
171     when(xmlContext.getConfig()).thenReturn(new ResTable_config());
172     when(xmlContext.getPackageName()).thenReturn("org.robolectric");
173 
174     TypedResource firstValue = new TypedResource<>("first_value", ResType.CHAR_SEQUENCE, xmlContext);
175     TypedResource secondValue = new TypedResource<>("second_value", ResType.CHAR_SEQUENCE, xmlContext);
176     bundle.put(new ResName("org.robolectric", "string", "resource_name"), firstValue);
177     bundle.put(new ResName("org.robolectric", "string", "resource_name"), secondValue);
178 
179     assertThat(bundle.get(new ResName("org.robolectric", "string", "resource_name"), from("")).getData()).isEqualTo("first_value");
180   }
181 
asResMap(String... qualifierses)182   private ResBundle.ResMap asResMap(String... qualifierses) {
183     ResBundle.ResMap resMap = new ResBundle.ResMap();
184     for (String qualifiers : qualifierses) {
185       resMap.put(resName, createStringTypedResource(qualifiers, qualifiers));
186     }
187     return resMap;
188   }
189 
createStringTypedResource(String qualifiers)190   private static TypedResource<String> createStringTypedResource(String qualifiers) {
191     return createStringTypedResource("title from resourceLoader1", qualifiers);
192   }
193 
194   @Nonnull
createStringTypedResource(String str, String qualifiersStr)195   private static TypedResource<String> createStringTypedResource(String str, String qualifiersStr) {
196     XmlContext mockXmlContext = mock(XmlContext.class);
197     Qualifiers qualifiers = Qualifiers.parse(qualifiersStr);
198     when(mockXmlContext.getQualifiers()).thenReturn(qualifiers);
199     when(mockXmlContext.getConfig()).thenReturn(qualifiers.getConfig());
200     return new TypedResource<>(str, ResType.CHAR_SEQUENCE, mockXmlContext);
201   }
202 
from(String qualifiers)203   private static ResTable_config from(String qualifiers) {
204     ResTable_config config = new ResTable_config();
205     if (!Strings.isNullOrEmpty(qualifiers) &&
206         !ConfigDescription.parse(qualifiers, config, false)) {
207       throw new IllegalArgumentException("Invalid qualifiers \"" + qualifiers + "\"");
208     }
209     return config;
210   }
211 }