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 }