• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.content.res;
2 
3 import static android.os.Build.VERSION_CODES.KITKAT;
4 import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
5 import static android.os.Build.VERSION_CODES.LOLLIPOP;
6 import static android.os.Build.VERSION_CODES.O;
7 import static android.os.Build.VERSION_CODES.Q;
8 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
9 import static android.util.TypedValue.COMPLEX_UNIT_IN;
10 import static android.util.TypedValue.COMPLEX_UNIT_MM;
11 import static android.util.TypedValue.COMPLEX_UNIT_PT;
12 import static android.util.TypedValue.COMPLEX_UNIT_PX;
13 import static android.util.TypedValue.COMPLEX_UNIT_SP;
14 import static android.util.TypedValue.TYPE_FIRST_COLOR_INT;
15 import static android.util.TypedValue.TYPE_INT_BOOLEAN;
16 import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
17 import static android.util.TypedValue.TYPE_INT_COLOR_RGB8;
18 import static android.util.TypedValue.TYPE_INT_DEC;
19 import static android.util.TypedValue.TYPE_LAST_INT;
20 import static android.util.TypedValue.TYPE_REFERENCE;
21 import static android.util.TypedValue.TYPE_STRING;
22 import static android.util.TypedValue.applyDimension;
23 import static com.google.common.truth.Truth.assertThat;
24 import static org.junit.Assert.assertThrows;
25 import static org.junit.Assert.fail;
26 import static org.junit.Assume.assumeFalse;
27 import static org.robolectric.testapp.R.color.test_ARGB8;
28 import static org.robolectric.testapp.R.color.test_RGB8;
29 
30 import android.content.Context;
31 import android.graphics.Bitmap;
32 import android.graphics.BitmapFactory;
33 import android.graphics.Color;
34 import android.graphics.Typeface;
35 import android.graphics.drawable.AnimationDrawable;
36 import android.graphics.drawable.BitmapDrawable;
37 import android.graphics.drawable.ColorDrawable;
38 import android.graphics.drawable.Drawable;
39 import android.graphics.drawable.NinePatchDrawable;
40 import android.graphics.fonts.Font;
41 import android.graphics.fonts.FontFamily;
42 import android.os.Build;
43 import android.util.AttributeSet;
44 import android.util.TypedValue;
45 import android.util.Xml;
46 import androidx.test.core.app.ApplicationProvider;
47 import androidx.test.ext.junit.runners.AndroidJUnit4;
48 import androidx.test.filters.SdkSuppress;
49 import com.google.common.collect.Range;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.lang.reflect.Method;
53 import org.junit.Before;
54 import org.junit.Ignore;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.robolectric.annotation.Config;
58 import org.robolectric.annotation.internal.DoNotInstrument;
59 import org.robolectric.testapp.R;
60 import org.xmlpull.v1.XmlPullParser;
61 import org.xmlpull.v1.XmlPullParserException;
62 
63 /**
64  * Compatibility test for {@link Resources}
65  */
66 @DoNotInstrument
67 @RunWith(AndroidJUnit4.class)
68 public class ResourcesTest {
69   private Resources resources;
70   private Context context;
71 
72   @Before
setup()73   public void setup() {
74     context = ApplicationProvider.getApplicationContext();
75     resources = context.getResources();
76   }
77 
78   @Test
getString()79   public void getString() {
80     assertThat(resources.getString(R.string.hello)).isEqualTo("Hello");
81     assertThat(resources.getString(R.string.say_it_with_item)).isEqualTo("flowers");
82   }
83 
84   @Test
getString_withReference()85   public void getString_withReference() {
86     assertThat(resources.getString(R.string.greeting)).isEqualTo("Howdy");
87   }
88 
89   @Test
getString_withInterpolation()90   public void getString_withInterpolation() {
91     assertThat(resources.getString(R.string.interpolate, "value")).isEqualTo("Here is a value!");
92   }
93 
94   @Test
getString_withHtml()95   public void getString_withHtml() {
96     assertThat(resources.getString(R.string.some_html, "value")).isEqualTo("Hello, world");
97   }
98 
99   @Test
getString_withSurroundingQuotes()100   public void getString_withSurroundingQuotes() {
101     assertThat(resources.getString(R.string.surrounding_quotes, "value")).isEqualTo("This'll work");
102   }
103 
104   @Test
getStringWithEscapedApostrophes()105   public void getStringWithEscapedApostrophes() {
106     assertThat(resources.getString(R.string.escaped_apostrophe)).isEqualTo("This'll also work");
107   }
108 
109   @Test
getStringWithEscapedQuotes()110   public void getStringWithEscapedQuotes() {
111     assertThat(resources.getString(R.string.escaped_quotes)).isEqualTo("Click \"OK\"");
112   }
113 
114   @Test
getString_StringWithInlinedQuotesAreStripped()115   public void getString_StringWithInlinedQuotesAreStripped() {
116     assertThat(resources.getString(R.string.bad_example)).isEqualTo("This is a bad string.");
117   }
118 
119   @Test
getStringShouldStripNewLines()120   public void getStringShouldStripNewLines() {
121     assertThat(resources.getString(R.string.leading_and_trailing_new_lines)).isEqualTo("Some text");
122   }
123 
124   @Test
preserveEscapedNewlineAndTab()125   public void preserveEscapedNewlineAndTab() {
126     assertThat(resources.getString(R.string.new_lines_and_tabs, 4)).isEqualTo("4\tmph\nfaster");
127   }
128 
129   @Test
getStringShouldConvertCodePoints()130   public void getStringShouldConvertCodePoints() {
131     assertThat(resources.getString(R.string.non_breaking_space)).isEqualTo("Closing"
132                                                                                + " soon:\u00A05pm");
133     assertThat(resources.getString(R.string.space)).isEqualTo("Closing soon: 5pm");
134   }
135 
136   @Test
getMultilineLayoutResource_shouldResolveLayoutReferencesWithLineBreaks()137   public void getMultilineLayoutResource_shouldResolveLayoutReferencesWithLineBreaks() {
138     // multiline_layout is a layout reference to activity_main layout.
139     TypedValue multilineLayoutValue = new TypedValue();
140     resources.getValue(R.layout.multiline_layout, multilineLayoutValue, true /* resolveRefs */);
141     TypedValue mainActivityLayoutValue = new TypedValue();
142     resources.getValue(R.layout.activity_main, mainActivityLayoutValue, false /* resolveRefs */);
143     assertThat(multilineLayoutValue.string).isEqualTo(mainActivityLayoutValue.string);
144   }
145 
146   @Test
getText_withHtml()147   public void getText_withHtml() {
148     assertThat(resources.getText(R.string.some_html, "value").toString()).isEqualTo("Hello, world");
149     // TODO: Raw resources have lost the tags early, but the following call should return a
150     // SpannedString
151     // assertThat(resources.getText(R.string.some_html)).isInstanceOf(SpannedString.class);
152   }
153 
154   @Test
getText_plainString()155   public void getText_plainString() {
156     assertThat(resources.getText(R.string.hello, "value").toString()).isEqualTo("Hello");
157     assertThat(resources.getText(R.string.hello)).isInstanceOf(String.class);
158   }
159 
160   @Test
getText_withLayoutId()161   public void getText_withLayoutId() {
162     // This isn't _really_ supported by the platform (gives a lint warning that getText() expects a
163     // String resource type
164     // but the actual platform behaviour is to return a string that equals
165     // "res/layout/layout_file.xml" so the current
166     // Robolectric behaviour deviates from the platform as we append the full file path from the
167     // current working directory.
168     String textString = resources.getText(R.layout.different_screen_sizes, "value").toString();
169     assertThat(textString).containsMatch("/different_screen_sizes.xml$");
170     // If we run tests on devices with different config, the resource system will select different
171     // layout directories.
172     assertThat(textString).containsMatch("^res/layout");
173   }
174 
175   @Test
getStringArray()176   public void getStringArray() {
177     assertThat(resources.getStringArray(R.array.items)).isEqualTo(new String[]{"foo", "bar"});
178     assertThat(resources.getStringArray(R.array.greetings))
179         .isEqualTo(new String[] {"hola", "Hello"});
180   }
181 
182   @Test
withIdReferenceEntry_obtainTypedArray()183   public void withIdReferenceEntry_obtainTypedArray() {
184     TypedArray typedArray = resources.obtainTypedArray(R.array.typed_array_with_resource_id);
185     assertThat(typedArray.length()).isEqualTo(2);
186 
187     assertThat(typedArray.getResourceId(0, 0)).isEqualTo(R.id.id_declared_in_item_tag);
188     assertThat(typedArray.getResourceId(1, 0)).isEqualTo(R.id.id_declared_in_layout);
189   }
190 
191   @Test
obtainTypedArray()192   public void obtainTypedArray() {
193     final TypedArray valuesTypedArray = resources.obtainTypedArray(R.array.typed_array_values);
194     assertThat(valuesTypedArray.getString(0)).isEqualTo("abcdefg");
195     assertThat(valuesTypedArray.getInt(1, 0)).isEqualTo(3875);
196     assertThat(valuesTypedArray.getInteger(1, 0)).isEqualTo(3875);
197     assertThat(valuesTypedArray.getFloat(2, 0.0f)).isEqualTo(2.0f);
198     assertThat(valuesTypedArray.getColor(3, Color.BLACK)).isEqualTo(Color.MAGENTA);
199     assertThat(valuesTypedArray.getColor(4, Color.BLACK)).isEqualTo(Color.parseColor("#00ffff"));
200     assertThat(valuesTypedArray.getDimension(5, 0.0f))
201         .isEqualTo(applyDimension(COMPLEX_UNIT_PX, 8, resources.getDisplayMetrics()));
202     assertThat(valuesTypedArray.getDimension(6, 0.0f))
203         .isEqualTo(applyDimension(COMPLEX_UNIT_DIP, 12, resources.getDisplayMetrics()));
204     assertThat(valuesTypedArray.getDimension(7, 0.0f))
205         .isEqualTo(applyDimension(COMPLEX_UNIT_DIP, 6, resources.getDisplayMetrics()));
206     assertThat(valuesTypedArray.getDimension(8, 0.0f))
207         .isEqualTo(applyDimension(COMPLEX_UNIT_MM, 3, resources.getDisplayMetrics()));
208     assertThat(valuesTypedArray.getDimension(9, 0.0f))
209         .isEqualTo(applyDimension(COMPLEX_UNIT_IN, 4, resources.getDisplayMetrics()));
210     assertThat(valuesTypedArray.getDimension(10, 0.0f))
211         .isEqualTo(applyDimension(COMPLEX_UNIT_SP, 36, resources.getDisplayMetrics()));
212     assertThat(valuesTypedArray.getDimension(11, 0.0f))
213         .isEqualTo(applyDimension(COMPLEX_UNIT_PT, 18, resources.getDisplayMetrics()));
214 
215     final TypedArray refsTypedArray = resources.obtainTypedArray(R.array.typed_array_references);
216     assertThat(refsTypedArray.getString(0)).isEqualTo("apple");
217     assertThat(refsTypedArray.getString(1)).isEqualTo("banana");
218     assertThat(refsTypedArray.getInt(2, 0)).isEqualTo(5);
219     assertThat(refsTypedArray.getBoolean(3, false)).isTrue();
220 
221     assertThat(refsTypedArray.getResourceId(8, 0)).isEqualTo(R.array.string_array_values);
222     assertThat(refsTypedArray.getTextArray(8))
223         .asList()
224         .containsAtLeast(
225             "abcdefg",
226             "3875",
227             "2.0",
228             "#ffff00ff",
229             "#00ffff",
230             "8px",
231             "12dp",
232             "6dip",
233             "3mm",
234             "4in",
235             "36sp",
236             "18pt");
237 
238     assertThat(refsTypedArray.getResourceId(9, 0)).isEqualTo(R.style.Theme_Robolectric);
239   }
240 
241   @Test
getInt()242   public void getInt() {
243     assertThat(resources.getInteger(R.integer.meaning_of_life)).isEqualTo(42);
244     assertThat(resources.getInteger(R.integer.test_integer1)).isEqualTo(2000);
245     assertThat(resources.getInteger(R.integer.test_integer2)).isEqualTo(9);
246     assertThat(resources.getInteger(R.integer.test_large_hex)).isEqualTo(-65536);
247     assertThat(resources.getInteger(R.integer.test_value_with_zero)).isEqualTo(7210);
248     assertThat(resources.getInteger(R.integer.meaning_of_life_as_item)).isEqualTo(42);
249   }
250 
251   @Test
getInt_withReference()252   public void getInt_withReference() {
253     assertThat(resources.getInteger(R.integer.reference_to_meaning_of_life)).isEqualTo(42);
254   }
255 
256   @Test
getIntArray()257   public void getIntArray() {
258     assertThat(resources.getIntArray(R.array.empty_int_array)).isEqualTo(new int[]{});
259     assertThat(resources.getIntArray(R.array.zero_to_four_int_array)).isEqualTo(new int[]{0, 1, 2, 3, 4});
260     assertThat(resources.getIntArray(R.array.with_references_int_array)).isEqualTo(new int[]{0, 2000, 1});
261     assertThat(resources.getIntArray(R.array.referenced_colors_int_array)).isEqualTo(new int[]{0x1, 0xFFFFFFFF, 0xFF000000, 0xFFF5F5F5, 0x802C76AD});
262   }
263 
264   @Test
getBoolean()265   public void getBoolean() {
266     assertThat(resources.getBoolean(R.bool.false_bool_value)).isEqualTo(false);
267     assertThat(resources.getBoolean(R.bool.true_as_item)).isEqualTo(true);
268   }
269 
270   @Test
getBoolean_withReference()271   public void getBoolean_withReference() {
272     assertThat(resources.getBoolean(R.bool.reference_to_true)).isEqualTo(true);
273   }
274 
275   @Test
getDimension()276   public void getDimension() {
277     assertThat(resources.getDimension(R.dimen.test_dip_dimen))
278         .isEqualTo(applyDimension(COMPLEX_UNIT_DIP, 20, resources.getDisplayMetrics()));
279     assertThat(resources.getDimension(R.dimen.test_dp_dimen))
280         .isEqualTo(applyDimension(COMPLEX_UNIT_DIP, 8, resources.getDisplayMetrics()));
281     assertThat(resources.getDimension(R.dimen.test_in_dimen))
282         .isEqualTo(applyDimension(COMPLEX_UNIT_IN, 99, resources.getDisplayMetrics()));
283     assertThat(resources.getDimension(R.dimen.test_mm_dimen))
284         .isEqualTo(applyDimension(COMPLEX_UNIT_MM, 42, resources.getDisplayMetrics()));
285     assertThat(resources.getDimension(R.dimen.test_px_dimen))
286         .isEqualTo(applyDimension(COMPLEX_UNIT_PX, 15, resources.getDisplayMetrics()));
287     assertThat(resources.getDimension(R.dimen.test_pt_dimen))
288         .isEqualTo(applyDimension(COMPLEX_UNIT_PT, 12, resources.getDisplayMetrics()));
289     assertThat(resources.getDimension(R.dimen.test_sp_dimen))
290         .isEqualTo(applyDimension(COMPLEX_UNIT_SP, 5, resources.getDisplayMetrics()));
291   }
292 
293   @Test
getDimensionPixelSize()294   public void getDimensionPixelSize() {
295     assertThat(resources.getDimensionPixelSize(R.dimen.test_dip_dimen))
296         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_DIP, 20)));
297     assertThat(resources.getDimensionPixelSize(R.dimen.test_dp_dimen))
298         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_DIP, 8)));
299     assertThat(resources.getDimensionPixelSize(R.dimen.test_in_dimen))
300         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_IN, 99)));
301     assertThat(resources.getDimensionPixelSize(R.dimen.test_mm_dimen))
302         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_MM, 42)));
303     assertThat(resources.getDimensionPixelSize(R.dimen.test_px_dimen))
304         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_PX, 15)));
305     assertThat(resources.getDimensionPixelSize(R.dimen.test_pt_dimen))
306         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_PT, 12)));
307     assertThat(resources.getDimensionPixelSize(R.dimen.test_sp_dimen))
308         .isIn(onePixelOf(convertDimension(COMPLEX_UNIT_SP, 5)));
309   }
310 
onePixelOf(int i)311   private static Range<Integer> onePixelOf(int i) {
312     return Range.closed(i - 1, i + 1);
313   }
314 
315   @Test
getDimensionPixelOffset()316   public void getDimensionPixelOffset() {
317     assertThat(resources.getDimensionPixelOffset(R.dimen.test_dip_dimen))
318         .isEqualTo(convertDimension(COMPLEX_UNIT_DIP, 20));
319     assertThat(resources.getDimensionPixelOffset(R.dimen.test_dp_dimen))
320         .isEqualTo(convertDimension(COMPLEX_UNIT_DIP, 8));
321     assertThat(resources.getDimensionPixelOffset(R.dimen.test_in_dimen))
322         .isEqualTo(convertDimension(COMPLEX_UNIT_IN, 99));
323     assertThat(resources.getDimensionPixelOffset(R.dimen.test_mm_dimen))
324         .isEqualTo(convertDimension(COMPLEX_UNIT_MM, 42));
325     assertThat(resources.getDimensionPixelOffset(R.dimen.test_px_dimen))
326         .isEqualTo(convertDimension(COMPLEX_UNIT_PX, 15));
327     assertThat(resources.getDimensionPixelOffset(R.dimen.test_pt_dimen))
328         .isEqualTo(convertDimension(COMPLEX_UNIT_PT, 12));
329     assertThat(resources.getDimensionPixelOffset(R.dimen.test_sp_dimen))
330         .isEqualTo(convertDimension(COMPLEX_UNIT_SP, 5));
331   }
332 
convertDimension(int unit, float value)333   private int convertDimension(int unit, float value) {
334     return (int) applyDimension(unit, value, resources.getDisplayMetrics());
335   }
336 
337   @Test
getDimension_withReference()338   public void getDimension_withReference() {
339     assertThat(resources.getBoolean(R.bool.reference_to_true)).isEqualTo(true);
340   }
341 
342   @Test
getStringArray_shouldThrowExceptionIfNotFound()343   public void getStringArray_shouldThrowExceptionIfNotFound() {
344     assertThrows(Resources.NotFoundException.class, () -> resources.getStringArray(-1));
345   }
346 
347   @Test
getIntegerArray_shouldThrowExceptionIfNotFound()348   public void getIntegerArray_shouldThrowExceptionIfNotFound() {
349     assertThrows(Resources.NotFoundException.class, () -> resources.getIntArray(-1));
350   }
351 
352   @Test
getQuantityString()353   public void getQuantityString() {
354     assertThat(resources.getQuantityString(R.plurals.beer, 1)).isEqualTo("a beer");
355     assertThat(resources.getQuantityString(R.plurals.beer, 2)).isEqualTo("some beers");
356     assertThat(resources.getQuantityString(R.plurals.beer, 3)).isEqualTo("some beers");
357   }
358 
359   @Test
getQuantityText()360   public void getQuantityText() {
361     // Feature not supported in legacy (raw) resource mode.
362     assumeFalse(isRobolectricLegacyMode());
363 
364     assertThat(resources.getQuantityText(R.plurals.beer, 1)).isEqualTo("a beer");
365     assertThat(resources.getQuantityText(R.plurals.beer, 2)).isEqualTo("some beers");
366     assertThat(resources.getQuantityText(R.plurals.beer, 3)).isEqualTo("some beers");
367   }
368 
369   @Test
getFraction()370   public void getFraction() {
371     final int myself = 300;
372     final int myParent = 600;
373     assertThat(resources.getFraction(R.fraction.half, myself, myParent)).isEqualTo(150f);
374     assertThat(resources.getFraction(R.fraction.half_of_parent, myself, myParent)).isEqualTo(300f);
375 
376     assertThat(resources.getFraction(R.fraction.quarter_as_item, myself, myParent)).isEqualTo(75f);
377     assertThat(resources.getFraction(R.fraction.quarter_of_parent_as_item, myself, myParent)).isEqualTo(150f);
378 
379     assertThat(resources.getFraction(R.fraction.fifth_as_reference, myself, myParent)).isWithin(0.01f).of(60f);
380     assertThat(resources.getFraction(R.fraction.fifth_of_parent_as_reference, myself, myParent)).isWithin(0.01f).of(120f);
381   }
382 
383   @Test
testConfiguration()384   public void testConfiguration() {
385     Configuration configuration = resources.getConfiguration();
386     assertThat(configuration).isNotNull();
387     assertThat(configuration.locale).isNotNull();
388   }
389 
390   @Test
testConfigurationReturnsTheSameInstance()391   public void testConfigurationReturnsTheSameInstance() {
392     assertThat(resources.getConfiguration()).isSameInstanceAs(resources.getConfiguration());
393   }
394 
395   @Test
testNewTheme()396   public void testNewTheme() {
397     assertThat(resources.newTheme()).isNotNull();
398   }
399 
400   @Test
testGetDrawableNullRClass()401   public void testGetDrawableNullRClass() {
402     assertThrows(
403         Resources.NotFoundException.class,
404         () -> assertThat(resources.getDrawable(-12345)).isInstanceOf(BitmapDrawable.class));
405   }
406 
407   @Test
testGetAnimationDrawable()408   public void testGetAnimationDrawable() {
409     assertThat(resources.getDrawable(R.anim.animation_list)).isInstanceOf(AnimationDrawable.class);
410   }
411 
412   @Test
testGetColorDrawable()413   public void testGetColorDrawable() {
414     Drawable drawable = resources.getDrawable(R.color.color_with_alpha);
415     assertThat(drawable).isInstanceOf(ColorDrawable.class);
416     assertThat(((ColorDrawable) drawable).getColor()).isEqualTo(0x802C76AD);
417   }
418 
419   @Test
getColor()420   public void getColor() {
421     assertThat(resources.getColor(R.color.color_with_alpha)).isEqualTo(0x802C76AD);
422   }
423 
424   @Test
getColor_withReference()425   public void getColor_withReference() {
426     assertThat(resources.getColor(R.color.background)).isEqualTo(0xfff5f5f5);
427   }
428 
429   @Test
testGetColor_Missing()430   public void testGetColor_Missing() {
431     assertThrows(Resources.NotFoundException.class, () -> resources.getColor(11234));
432   }
433 
434   @Test
testGetColorStateList()435   public void testGetColorStateList() {
436     assertThat(resources.getColorStateList(R.color.color_state_list)).isInstanceOf(ColorStateList.class);
437   }
438 
439   @Test
testGetBitmapDrawable()440   public void testGetBitmapDrawable() {
441     assertThat(resources.getDrawable(R.drawable.an_image)).isInstanceOf(BitmapDrawable.class);
442   }
443 
444   @Test
testGetNinePatchDrawable()445   public void testGetNinePatchDrawable() {
446     assertThat(resources.getDrawable(R.drawable.nine_patch_drawable)).isInstanceOf(NinePatchDrawable.class);
447   }
448 
449   @Test
testGetBitmapDrawableForUnknownId()450   public void testGetBitmapDrawableForUnknownId() {
451     assertThrows(
452         Resources.NotFoundException.class,
453         () ->
454             assertThat(resources.getDrawable(Integer.MAX_VALUE))
455                 .isInstanceOf(BitmapDrawable.class));
456   }
457 
458   @Test
testGetNinePatchDrawableIntrinsicWidth()459   public void testGetNinePatchDrawableIntrinsicWidth() {
460     float density = resources.getDisplayMetrics().density;
461     NinePatchDrawable ninePatchDrawable =
462         (NinePatchDrawable) resources.getDrawable(R.drawable.nine_patch_drawable);
463     // Use Math.round to convert calculated float width to int,
464     // see NinePatchDrawable#scaleFromDensity.
465     assertThat(ninePatchDrawable.getIntrinsicWidth()).isEqualTo(Math.round(98.0f * density));
466   }
467 
468   @Test
testGetIdentifier()469   public void testGetIdentifier() {
470 
471     final String resourceType = "string";
472     final String packageName = context.getPackageName();
473 
474     final String resourceName = "hello";
475     final int resId1 = resources.getIdentifier(resourceName, resourceType, packageName);
476     assertThat(resId1).isEqualTo(R.string.hello);
477 
478     final String typedResourceName = resourceType + "/" + resourceName;
479     final int resId2 = resources.getIdentifier(typedResourceName, resourceType, packageName);
480     assertThat(resId2).isEqualTo(R.string.hello);
481 
482     final String fqn = packageName + ":" + typedResourceName;
483     final int resId3 = resources.getIdentifier(fqn, resourceType, packageName);
484     assertThat(resId3).isEqualTo(R.string.hello);
485   }
486 
487   @Test
getIdentifier()488   public void getIdentifier() {
489     String string = resources.getString(R.string.hello);
490     assertThat(string).isEqualTo("Hello");
491 
492 
493     int id = resources.getIdentifier("hello", "string", context.getPackageName());
494     assertThat(id).isEqualTo(R.string.hello);
495 
496     String hello = resources.getString(id);
497     assertThat(hello).isEqualTo("Hello");
498   }
499 
500   @Test
getIdentifier_nonExistantResource()501   public void getIdentifier_nonExistantResource() {
502     int id = resources.getIdentifier("just_alot_of_crap", "string", context.getPackageName());
503     assertThat(id).isEqualTo(0);
504   }
505 
506   @Test
507   @SdkSuppress(minSdkVersion = LOLLIPOP)
508   @Config(minSdk = LOLLIPOP)
getIdentifier_material()509   public void getIdentifier_material() {
510     int id = Resources.getSystem().getIdentifier("btn_check_material_anim", "drawable", "android");
511     assertThat(id).isGreaterThan(0);
512   }
513 
514   /**
515    * Public framework symbols are defined here:
516    * https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/public.xml
517    * Private framework symbols are defined here:
518    * https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/symbols.xml
519    *
520    * <p>These generate android.R and com.android.internal.R respectively, when Framework Java code
521    * does not need to reference a framework resource it will not have an R value generated.
522    * Robolectric is then missing an identifier for this resource so we must generate a placeholder
523    * ourselves.
524    */
525   @Test
526   // @Config(sdk = Build.VERSION_CODES.LOLLIPOP) // android:color/secondary_text_material_dark was
527   // added in API 21
528   @SdkSuppress(minSdkVersion = LOLLIPOP)
529   @Config(minSdk = LOLLIPOP)
shouldGenerateIdsForResourcesThatAreMissingRValues()530   public void shouldGenerateIdsForResourcesThatAreMissingRValues() {
531     int identifierMissingFromRFile =
532         resources.getIdentifier("secondary_text_material_dark", "color", "android");
533 
534     // We expect Robolectric to generate a placeholder identifier where one was not generated in the
535     // android R files.
536     assertThat(identifierMissingFromRFile).isNotEqualTo(0);
537 
538     // We expect to be able to successfully android:color/secondary_text_material_dark to a
539     // ColorStateList.
540     assertThat(resources.getColorStateList(identifierMissingFromRFile)).isNotNull();
541   }
542 
543   @Test
getSystemShouldReturnSystemResources()544   public void getSystemShouldReturnSystemResources() {
545     assertThat(Resources.getSystem()).isInstanceOf(Resources.class);
546   }
547 
548   @Test
multipleCallsToGetSystemShouldReturnSameInstance()549   public void multipleCallsToGetSystemShouldReturnSameInstance() {
550     assertThat(Resources.getSystem()).isEqualTo(Resources.getSystem());
551   }
552 
553   @Test
applicationResourcesShouldHaveBothSystemAndLocalValues()554   public void applicationResourcesShouldHaveBothSystemAndLocalValues() {
555     assertThat(context.getResources().getString(android.R.string.copy)).isEqualTo("Copy");
556     assertThat(context.getResources().getString(R.string.copy)).isEqualTo("Local Copy");
557   }
558 
559   @Test
systemResourcesShouldReturnCorrectSystemId()560   public void systemResourcesShouldReturnCorrectSystemId() {
561     assertThat(Resources.getSystem().getIdentifier("copy", "string", "android"))
562         .isEqualTo(android.R.string.copy);
563   }
564 
565   @Test
systemResourcesShouldReturnZeroForLocalId()566   public void systemResourcesShouldReturnZeroForLocalId() {
567     assertThat(Resources.getSystem().getIdentifier("copy", "string", context.getPackageName()))
568         .isEqualTo(0);
569   }
570 
571   @Test
testGetXml()572   public void testGetXml() throws Exception {
573     XmlResourceParser parser = resources.getXml(R.xml.preferences);
574     assertThat(parser).isNotNull();
575     assertThat(findRootTag(parser)).isEqualTo("PreferenceScreen");
576 
577     parser = resources.getXml(R.layout.custom_layout);
578     assertThat(parser).isNotNull();
579     assertThat(findRootTag(parser)).isEqualTo("org.robolectric.android.CustomView");
580 
581     parser = resources.getXml(R.menu.test);
582     assertThat(parser).isNotNull();
583     assertThat(findRootTag(parser)).isEqualTo("menu");
584 
585     parser = resources.getXml(R.drawable.rainbow);
586     assertThat(parser).isNotNull();
587     assertThat(findRootTag(parser)).isEqualTo("layer-list");
588 
589     parser = resources.getXml(R.anim.test_anim_1);
590     assertThat(parser).isNotNull();
591     assertThat(findRootTag(parser)).isEqualTo("set");
592 
593     parser = resources.getXml(R.color.color_state_list);
594     assertThat(parser).isNotNull();
595     assertThat(findRootTag(parser)).isEqualTo("selector");
596   }
597 
598   @Test
testGetXml_nonexistentResource()599   public void testGetXml_nonexistentResource() {
600     assertThrows(Resources.NotFoundException.class, () -> resources.getXml(0));
601   }
602 
603   @Test
testGetXml_nonxmlfile()604   public void testGetXml_nonxmlfile() {
605     assertThrows(Resources.NotFoundException.class, () -> resources.getXml(R.drawable.an_image));
606   }
607 
608   @Test
testGetXml_notNPEAfterClose()609   public void testGetXml_notNPEAfterClose() {
610     XmlResourceParser parser = resources.getXml(R.xml.preferences);
611     parser.close();
612     // the following methods should not NPE if the XmlResourceParser has been closed.
613     assertThat(parser.getName()).isNull();
614     assertThat(parser.getNamespace()).isEmpty();
615     assertThat(parser.getText()).isNull();
616   }
617 
618   @Test
openRawResource_shouldLoadRawResources()619   public void openRawResource_shouldLoadRawResources() {
620     InputStream resourceStream = resources.openRawResource(R.raw.raw_resource);
621     assertThat(resourceStream).isNotNull();
622     // assertThat(TestUtil.readString(resourceStream)).isEqualTo("raw txt file contents");
623   }
624 
625   @Test
openRawResource_shouldLoadDrawables()626   public void openRawResource_shouldLoadDrawables() {
627     InputStream resourceStream = resources.openRawResource(R.drawable.an_image);
628     Bitmap bitmap = BitmapFactory.decodeStream(resourceStream);
629     assertThat(bitmap.getHeight()).isEqualTo(53);
630     assertThat(bitmap.getWidth()).isEqualTo(64);
631   }
632 
633   @Test
openRawResource_withNonFile_throwsNotFoundException()634   public void openRawResource_withNonFile_throwsNotFoundException() {
635     try {
636       resources.openRawResource(R.string.hello);
637       fail("should throw");
638     } catch (Resources.NotFoundException e) {
639       // cool
640     }
641 
642     try {
643       resources.openRawResource(R.string.hello, new TypedValue());
644       fail("should throw");
645     } catch (Resources.NotFoundException e) {
646       // cool
647     }
648 
649     try {
650       resources.openRawResource(-1234, new TypedValue());
651       fail("should throw");
652     } catch (Resources.NotFoundException e) {
653       // cool
654     }
655   }
656 
657   @Test
openRawResourceFd_withNonCompressedFile_returnsNotNull()658   public void openRawResourceFd_withNonCompressedFile_returnsNotNull() throws IOException {
659     // This test will run on non-legacy resource mode in Robolectric environment.
660     // To test behavior on legacy mode environment, please see ShadowResourceTest.
661     try (AssetFileDescriptor afd = resources.openRawResourceFd(R.raw.raw_resource)) {
662       assertThat(afd).isNotNull();
663     }
664   }
665 
666   @Test
openRawResourceFd_withNonFile_throwsNotFoundException()667   public void openRawResourceFd_withNonFile_throwsNotFoundException() {
668     try {
669       resources.openRawResourceFd(R.string.hello);
670       fail("should throw");
671     } catch (Resources.NotFoundException e) {
672       // cool
673     }
674 
675     try {
676       resources.openRawResourceFd(-1234);
677       fail("should throw");
678     } catch (Resources.NotFoundException e) {
679       // cool
680     }
681   }
682 
683   @Test
getXml_withNonFile_throwsNotFoundException()684   public void getXml_withNonFile_throwsNotFoundException() {
685     try {
686       resources.getXml(R.string.hello);
687       fail("should throw");
688     } catch (Resources.NotFoundException e) {
689       // cool
690     }
691 
692     try {
693       resources.getXml(-1234);
694       fail("should throw");
695     } catch (Resources.NotFoundException e) {
696       // cool
697     }
698   }
699 
700   @Test
themeResolveAttribute_shouldSupportNotDereferencingResource()701   public void themeResolveAttribute_shouldSupportNotDereferencingResource() {
702     TypedValue out = new TypedValue();
703 
704     Resources.Theme theme = resources.newTheme();
705     theme.applyStyle(R.style.MyBlackTheme, false);
706 
707     theme.resolveAttribute(android.R.attr.windowBackground, out, false);
708     assertThat(out.type).isEqualTo(TYPE_REFERENCE);
709     assertThat(out.data).isEqualTo(android.R.color.black);
710   }
711 
712   @Test
obtainAttributes_shouldReturnValuesFromResources()713   public void obtainAttributes_shouldReturnValuesFromResources() throws Exception {
714     XmlPullParser parser = resources.getXml(R.xml.xml_attrs);
715     parser.next();
716     parser.next();
717     AttributeSet attributes = Xml.asAttributeSet(parser);
718 
719     TypedArray typedArray =
720         resources.obtainAttributes(
721             attributes, new int[] {android.R.attr.title, android.R.attr.scrollbarFadeDuration});
722 
723     assertThat(typedArray.getString(0)).isEqualTo("Android Title");
724     assertThat(typedArray.getInt(1, 0)).isEqualTo(1111);
725     typedArray.recycle();
726   }
727 
728   // @Test
729   // public void obtainAttributes_shouldUseReferencedIdFromAttributeSet() throws Exception {
730   //   // android:id/mask was introduced in API 21, but it's still possible for apps built against API 21 to refer to it
731   //   // in older runtimes because referenced resource ids are compiled (by aapt) into the binary XML format.
732   //   AttributeSet attributeSet = Robolectric.buildAttributeSet()
733   //       .addAttribute(android.R.attr.id, "@android:id/mask").build();
734   //   TypedArray typedArray = resources.obtainAttributes(attributeSet, new int[]{android.R.attr.id});
735   //   assertThat(typedArray.getResourceId(0, -9)).isEqualTo(android.R.id.mask);
736   // }
737 
738   @Test
obtainStyledAttributesShouldDereferenceValues()739   public void obtainStyledAttributesShouldDereferenceValues() {
740     Resources.Theme theme = resources.newTheme();
741     theme.applyStyle(R.style.MyBlackTheme, false);
742     TypedArray arr = theme.obtainStyledAttributes(new int[]{android.R.attr.windowBackground});
743     TypedValue value = new TypedValue();
744     arr.getValue(0, value);
745     arr.recycle();
746 
747     assertThat(value.type).isAtLeast(TYPE_FIRST_COLOR_INT);
748     assertThat(value.type).isAtMost(TYPE_LAST_INT);
749   }
750 
751   // @Test
752   // public void obtainStyledAttributes_shouldCheckXmlFirst_fromAttributeSetBuilder() throws Exception {
753   //
754   //   // This simulates a ResourceProvider built from a 21+ SDK as viewportHeight / viewportWidth were introduced in API 21
755   //   // but the public ID values they are assigned clash with private com.android.internal.R values on older SDKs. This
756   //   // test ensures that even on older SDKs, on calls to obtainStyledAttributes() Robolectric will first check for matching
757   //   // resource ID values in the AttributeSet before checking the theme.
758   //
759   //   AttributeSet attributes = Robolectric.buildAttributeSet()
760   //       .addAttribute(android.R.attr.viewportWidth, "12.0")
761   //       .addAttribute(android.R.attr.viewportHeight, "24.0")
762   //       .build();
763   //
764   //   TypedArray typedArray = context.getTheme().obtainStyledAttributes(attributes, new int[] {
765   //       android.R.attr.viewportWidth,
766   //       android.R.attr.viewportHeight
767   //   }, 0, 0);
768   //   assertThat(typedArray.getFloat(0, 0)).isEqualTo(12.0f);
769   //   assertThat(typedArray.getFloat(1, 0)).isEqualTo(24.0f);
770   //   typedArray.recycle();
771   // }
772 
773   @Test
obtainStyledAttributes_shouldCheckXmlFirst_fromXmlLoadedFromResources()774   public void obtainStyledAttributes_shouldCheckXmlFirst_fromXmlLoadedFromResources() throws Exception {
775 
776     // This simulates a ResourceProvider built from a 21+ SDK as viewportHeight / viewportWidth were introduced in API 21
777     // but the public ID values they are assigned clash with private com.android.internal.R values on older SDKs. This
778     // test ensures that even on older SDKs, on calls to obtainStyledAttributes() Robolectric will first check for matching
779     // resource ID values in the AttributeSet before checking the theme.
780 
781     XmlResourceParser xml = context.getResources().getXml(R.drawable.vector);
782     xml.next();
783     xml.next();
784     AttributeSet attributeSet = Xml.asAttributeSet(xml);
785 
786     TypedArray typedArray = context.getTheme().obtainStyledAttributes(attributeSet, new int[] {
787         android.R.attr.viewportWidth,
788         android.R.attr.viewportHeight
789     }, 0, 0);
790     assertThat(typedArray.getFloat(0, 0)).isEqualTo(12.0f);
791     assertThat(typedArray.getFloat(1, 0)).isEqualTo(24.0f);
792     typedArray.recycle();
793   }
794 
795   @Test
796   @SdkSuppress(minSdkVersion = LOLLIPOP)
797   @Config(minSdk = LOLLIPOP)
whenAttrIsDefinedInRuntimeSdk_getResourceName_findsResource()798   public void whenAttrIsDefinedInRuntimeSdk_getResourceName_findsResource() {
799     assertThat(context.getResources().getResourceName(android.R.attr.viewportHeight))
800         .isEqualTo("android:attr/viewportHeight");
801   }
802 
803   @Test
804   @SdkSuppress(maxSdkVersion = KITKAT)
805   @Config(maxSdk = KITKAT_WATCH)
whenAttrIsNotDefinedInRuntimeSdk_getResourceName_doesntFindRequestedResourceButInsteadFindsInternalResourceWithSameId()806   public void whenAttrIsNotDefinedInRuntimeSdk_getResourceName_doesntFindRequestedResourceButInsteadFindsInternalResourceWithSameId() {
807     // asking for an attr defined after the current SDK doesn't have a defined result; in this case it returns
808     //   numberPickerStyle from com.internal.android.R
809     assertThat(context.getResources().getResourceName(android.R.attr.viewportHeight))
810         .isNotEqualTo("android:attr/viewportHeight");
811 
812     assertThat(context.getResources().getIdentifier("viewportHeight", "attr", "android"))
813         .isEqualTo(0);
814   }
815 
816   @Test
subClassInitializedOK()817   public void subClassInitializedOK() {
818     SubClassResources subClassResources = new SubClassResources(resources);
819     assertThat(subClassResources.openRawResource(R.raw.raw_resource)).isNotNull();
820   }
821 
822   @Test
applyStyleForced()823   public void applyStyleForced() {
824     final Resources.Theme theme = resources.newTheme();
825 
826     theme.applyStyle(R.style.MyBlackTheme, true);
827     TypedArray arr = theme.obtainStyledAttributes(new int[]{android.R.attr.windowBackground, android.R.attr.textColorHint});
828 
829     final TypedValue blackBackgroundColor = new TypedValue();
830     arr.getValue(0, blackBackgroundColor);
831     assertThat(blackBackgroundColor.resourceId).isEqualTo(android.R.color.black);
832     arr.recycle();
833 
834     theme.applyStyle(R.style.MyBlueTheme, true);
835     arr = theme.obtainStyledAttributes(new int[]{android.R.attr.windowBackground, android.R.attr.textColor, android.R.attr.textColorHint});
836 
837     final TypedValue blueBackgroundColor = new TypedValue();
838     arr.getValue(0, blueBackgroundColor);
839     assertThat(blueBackgroundColor.resourceId).isEqualTo(R.color.blue);
840 
841     final TypedValue blueTextColor = new TypedValue();
842     arr.getValue(1, blueTextColor);
843     assertThat(blueTextColor.resourceId).isEqualTo(R.color.white);
844 
845     final TypedValue blueTextColorHint = new TypedValue();
846     arr.getValue(2, blueTextColorHint);
847     assertThat(blueTextColorHint.resourceId).isEqualTo(android.R.color.darker_gray);
848 
849     arr.recycle();
850   }
851 
852   @Test
applyStyleNotForced()853   public void applyStyleNotForced() {
854     final Resources.Theme theme = resources.newTheme();
855 
856     // Apply black theme
857     theme.applyStyle(R.style.MyBlackTheme, true);
858     TypedArray arr = theme.obtainStyledAttributes(new int[]{android.R.attr.windowBackground, android.R.attr.textColorHint});
859 
860     final TypedValue blackBackgroundColor = new TypedValue();
861     arr.getValue(0, blackBackgroundColor);
862     assertThat(blackBackgroundColor.resourceId).isEqualTo(android.R.color.black);
863 
864     final TypedValue blackTextColorHint = new TypedValue();
865     arr.getValue(1, blackTextColorHint);
866     assertThat(blackTextColorHint.resourceId).isEqualTo(android.R.color.darker_gray);
867 
868     arr.recycle();
869 
870     // Apply blue theme
871     theme.applyStyle(R.style.MyBlueTheme, false);
872     arr = theme.obtainStyledAttributes(new int[]{android.R.attr.windowBackground, android.R.attr.textColor, android.R.attr.textColorHint});
873 
874     final TypedValue blueBackgroundColor = new TypedValue();
875     arr.getValue(0, blueBackgroundColor);
876     assertThat(blueBackgroundColor.resourceId).isEqualTo(android.R.color.black);
877 
878     final TypedValue blueTextColor = new TypedValue();
879     arr.getValue(1, blueTextColor);
880     assertThat(blueTextColor.resourceId).isEqualTo(R.color.white);
881 
882     final TypedValue blueTextColorHint = new TypedValue();
883     arr.getValue(2, blueTextColorHint);
884     assertThat(blueTextColorHint.resourceId).isEqualTo(android.R.color.darker_gray);
885 
886     arr.recycle();
887   }
888 
889   @Test
getValueShouldClearTypedArrayBetweenCalls()890   public void getValueShouldClearTypedArrayBetweenCalls() {
891     TypedValue outValue = new TypedValue();
892 
893     resources.getValue(R.string.hello, outValue, true);
894     assertThat(outValue.type).isEqualTo(TYPE_STRING);
895     assertThat(outValue.string).isEqualTo(resources.getString(R.string.hello));
896     // outValue.data is an index into the String block which we don't know for raw xml resources.
897     assertThat(outValue.assetCookie).isNotEqualTo(0);
898 
899     resources.getValue(R.color.blue, outValue, true);
900     assertThat(outValue.type).isEqualTo(TYPE_INT_COLOR_RGB8);
901     assertThat(outValue.data).isEqualTo(0xFF0000FF);
902     assertThat(outValue.string).isNull();
903     // outValue.assetCookie is not supported with raw XML
904 
905     resources.getValue(R.integer.loneliest_number, outValue, true);
906     assertThat(outValue.type).isEqualTo(TYPE_INT_DEC);
907     assertThat(outValue.data).isEqualTo(1);
908     assertThat(outValue.string).isNull();
909 
910     resources.getValue(R.bool.true_bool_value, outValue, true);
911     assertThat(outValue.type).isEqualTo(TYPE_INT_BOOLEAN);
912     assertThat(outValue.data).isNotEqualTo(0); // true == traditionally 0xffffffff, -1 in Java but
913     // tests should be checking for non-zero
914     assertThat(outValue.string).isNull();
915   }
916 
917   @Test
getXml()918   public void getXml() throws Exception {
919     XmlResourceParser xmlResourceParser = resources.getXml(R.xml.preferences);
920     assertThat(xmlResourceParser).isNotNull();
921     assertThat(xmlResourceParser.next()).isEqualTo(XmlResourceParser.START_DOCUMENT);
922     assertThat(xmlResourceParser.next()).isEqualTo(XmlResourceParser.START_TAG);
923     assertThat(xmlResourceParser.getName()).isEqualTo("PreferenceScreen");
924   }
925 
926   @Test
getXml_shouldParseEmojiCorrectly()927   public void getXml_shouldParseEmojiCorrectly() throws IOException, XmlPullParserException {
928     XmlResourceParser xmlResourceParser = resources.getXml(R.xml.has_emoji);
929     xmlResourceParser.next();
930     xmlResourceParser.next();
931     assertThat(xmlResourceParser.getName()).isEqualTo("EmojiRoot");
932     AttributeSet attributeSet = Xml.asAttributeSet(xmlResourceParser);
933     assertThat(attributeSet.getAttributeValue(null, "label1")).isEqualTo("no emoji");
934     String pureEmoji = "\uD83D\uDE00";
935     assertThat(attributeSet.getAttributeValue(null, "label2")).isEqualTo(pureEmoji);
936     assertThat(attributeSet.getAttributeValue(null, "label3")).isEqualTo(pureEmoji);
937     String mixEmojiAndText = "\uD83D\uDE00internal1\uD83D\uDE00internal2\uD83D\uDE00";
938     assertThat(attributeSet.getAttributeValue(null, "label4")).isEqualTo(mixEmojiAndText);
939     assertThat(attributeSet.getAttributeValue(null, "label5")).isEqualTo(mixEmojiAndText);
940     assertThat(attributeSet.getAttributeValue(null, "label6"))
941         .isEqualTo("don't worry be \uD83D\uDE00");
942   }
943 
944   @Test
whenMissingXml_throwNotFoundException()945   public void whenMissingXml_throwNotFoundException() {
946     try {
947       resources.getXml(0x3038);
948       fail();
949     } catch (Resources.NotFoundException e) {
950       assertThat(e.getMessage()).contains("Resource ID #0x3038");
951     }
952   }
953 
954   @Test
stringWithSpaces()955   public void stringWithSpaces() {
956     // this differs from actual Android behavior, which collapses whitespace as "Up to 25 USD"
957     assertThat(resources.getString(R.string.string_with_spaces, "25", "USD"))
958         .isEqualTo("Up to 25 USD");
959   }
960 
961   @Test
internalWhiteSpaceShouldBeCollapsed()962   public void internalWhiteSpaceShouldBeCollapsed() {
963     assertThat(resources.getString(R.string.internal_whitespace_blocks))
964         .isEqualTo("Whitespace in" + " the middle");
965     assertThat(resources.getString(R.string.internal_newlines)).isEqualTo("Some Newlines");
966   }
967 
968   @Test
fontTagWithAttributesShouldBeRead()969   public void fontTagWithAttributesShouldBeRead() {
970     assertThat(resources.getString(R.string.font_tag_with_attribute))
971         .isEqualTo("This string has a font tag");
972   }
973 
974   @Test
linkTagWithAttributesShouldBeRead()975   public void linkTagWithAttributesShouldBeRead() {
976     assertThat(resources.getString(R.string.link_tag_with_attribute))
977         .isEqualTo("This string has a link tag");
978   }
979 
980   @Test
getResourceTypeName_mipmap()981   public void getResourceTypeName_mipmap() {
982     assertThat(resources.getResourceTypeName(R.mipmap.mipmap_reference)).isEqualTo("mipmap");
983     assertThat(resources.getResourceTypeName(R.mipmap.robolectric)).isEqualTo("mipmap");
984   }
985 
986   @Test
getDrawable_mipmapReferencesResolve()987   public void getDrawable_mipmapReferencesResolve() {
988     Drawable reference = resources.getDrawable(R.mipmap.mipmap_reference);
989     Drawable original = resources.getDrawable(R.mipmap.robolectric);
990 
991     assertThat(reference.getMinimumHeight()).isEqualTo(original.getMinimumHeight());
992     assertThat(reference.getMinimumWidth()).isEqualTo(original.getMinimumWidth());
993   }
994 
995   @Test
996   @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
997   @Config(minSdk = Build.VERSION_CODES.O)
getDrawable_mipmapReferencesResolveXml()998   public void getDrawable_mipmapReferencesResolveXml() {
999     Drawable reference = resources.getDrawable(R.mipmap.robolectric_xml);
1000     Drawable original = resources.getDrawable(R.mipmap.mipmap_reference_xml);
1001 
1002     assertThat(reference.getMinimumHeight()).isEqualTo(original.getMinimumHeight());
1003     assertThat(reference.getMinimumWidth()).isEqualTo(original.getMinimumWidth());
1004   }
1005 
1006   @Test
forUntouchedThemes_copyTheme_shouldCopyNothing()1007   public void forUntouchedThemes_copyTheme_shouldCopyNothing() {
1008     Resources.Theme theme1 = resources.newTheme();
1009     Resources.Theme theme2 = resources.newTheme();
1010     theme2.setTo(theme1);
1011   }
1012 
1013   @Test
getResourceIdentifier_shouldReturnValueFromRClass()1014   public void getResourceIdentifier_shouldReturnValueFromRClass() {
1015     assertThat(
1016         resources.getIdentifier("id_declared_in_item_tag", "id", context.getPackageName()))
1017         .isEqualTo(R.id.id_declared_in_item_tag);
1018     assertThat(
1019         resources.getIdentifier("id/id_declared_in_item_tag", null, context.getPackageName()))
1020         .isEqualTo(R.id.id_declared_in_item_tag);
1021     assertThat(
1022         resources.getIdentifier(context.getPackageName() + ":id_declared_in_item_tag", "id", null))
1023         .isEqualTo(R.id.id_declared_in_item_tag);
1024     assertThat(
1025             resources.getIdentifier(
1026                 context.getPackageName() + ":id/id_declared_in_item_tag", "other", "other"))
1027         .isEqualTo(R.id.id_declared_in_item_tag);
1028   }
1029 
1030   @Test
whenPackageIsUnknown_getResourceIdentifier_shouldReturnZero()1031   public void whenPackageIsUnknown_getResourceIdentifier_shouldReturnZero() {
1032     assertThat(
1033         resources.getIdentifier("whatever", "id", "some.unknown.package"))
1034         .isEqualTo(0);
1035     assertThat(
1036         resources.getIdentifier("id/whatever", null, "some.unknown.package"))
1037         .isEqualTo(0);
1038     assertThat(
1039         resources.getIdentifier("some.unknown.package:whatever", "id", null))
1040         .isEqualTo(0);
1041     assertThat(
1042         resources.getIdentifier("some.unknown.package:id/whatever", "other", "other"))
1043         .isEqualTo(0);
1044 
1045     assertThat(
1046         resources.getIdentifier("whatever", "drawable", "some.unknown.package"))
1047         .isEqualTo(0);
1048     assertThat(
1049         resources.getIdentifier("drawable/whatever", null, "some.unknown.package"))
1050         .isEqualTo(0);
1051     assertThat(
1052         resources.getIdentifier("some.unknown.package:whatever", "drawable", null))
1053         .isEqualTo(0);
1054     assertThat(
1055         resources.getIdentifier("some.unknown.package:id/whatever", "other", "other"))
1056         .isEqualTo(0);
1057   }
1058 
1059   @Test
1060   @Ignore(
1061       "currently ids are always automatically assigned a value; to fix this we'd need to check "
1062           + "layouts for +@id/___, which is expensive")
whenCalledForIdWithNameNotInRClassOrXml_getResourceIdentifier_shouldReturnZero()1063   public void whenCalledForIdWithNameNotInRClassOrXml_getResourceIdentifier_shouldReturnZero() {
1064     assertThat(
1065         resources.getIdentifier(
1066             "org.robolectric:id/idThatDoesntExistAnywhere", "other", "other"))
1067         .isEqualTo(0);
1068   }
1069 
1070   @Test
1071   public void
whenIdIsAbsentInXmlButPresentInRClass_getResourceIdentifier_shouldReturnIdFromRClass_probablyBecauseItWasDeclaredInALayout()1072       whenIdIsAbsentInXmlButPresentInRClass_getResourceIdentifier_shouldReturnIdFromRClass_probablyBecauseItWasDeclaredInALayout() {
1073     assertThat(
1074         resources.getIdentifier("id_declared_in_layout", "id", context.getPackageName()))
1075         .isEqualTo(R.id.id_declared_in_layout);
1076   }
1077 
1078   @Test
whenResourceIsAbsentInXml_getResourceIdentifier_shouldReturn0()1079   public void whenResourceIsAbsentInXml_getResourceIdentifier_shouldReturn0() {
1080     assertThat(
1081         resources.getIdentifier("fictitiousDrawable", "drawable", context.getPackageName()))
1082         .isEqualTo(0);
1083   }
1084 
1085   @Test
whenResourceIsAbsentInXml_getResourceIdentifier_shouldReturnId()1086   public void whenResourceIsAbsentInXml_getResourceIdentifier_shouldReturnId() {
1087     assertThat(
1088         resources.getIdentifier("an_image", "drawable", context.getPackageName()))
1089         .isEqualTo(R.drawable.an_image);
1090   }
1091 
1092   @Test
whenResourceIsXml_getResourceIdentifier_shouldReturnId()1093   public void whenResourceIsXml_getResourceIdentifier_shouldReturnId() {
1094     assertThat(
1095         resources.getIdentifier("preferences", "xml", context.getPackageName()))
1096         .isEqualTo(R.xml.preferences);
1097   }
1098 
1099   @Test
whenResourceIsRaw_getResourceIdentifier_shouldReturnId()1100   public void whenResourceIsRaw_getResourceIdentifier_shouldReturnId() {
1101     assertThat(
1102         resources.getIdentifier("raw_resource", "raw", context.getPackageName()))
1103         .isEqualTo(R.raw.raw_resource);
1104   }
1105 
1106   @Test
getResourceValue_colorARGB8()1107   public void getResourceValue_colorARGB8() {
1108     TypedValue outValue = new TypedValue();
1109     resources.getValue(test_ARGB8, outValue, false);
1110     assertThat(outValue.type).isEqualTo(TYPE_INT_COLOR_ARGB8);
1111     assertThat(Color.blue(outValue.data)).isEqualTo(2);
1112   }
1113 
1114   @Test
getResourceValue_colorRGB8()1115   public void getResourceValue_colorRGB8() {
1116     TypedValue outValue = new TypedValue();
1117     resources.getValue(test_RGB8, outValue, false);
1118     assertThat(outValue.type).isEqualTo(TYPE_INT_COLOR_RGB8);
1119     assertThat(Color.blue(outValue.data)).isEqualTo(4);
1120   }
1121 
1122   @Test
getResourceEntryName_forStyle()1123   public void getResourceEntryName_forStyle() {
1124     assertThat(resources.getResourceEntryName(android.R.style.TextAppearance_Small))
1125         .isEqualTo("TextAppearance.Small");
1126   }
1127 
1128   @Test
1129   @SdkSuppress(minSdkVersion = O)
1130   @Config(minSdk = O)
getFont()1131   public void getFont() {
1132     // Feature not supported in legacy (raw) resource mode.
1133     assumeFalse(isRobolectricLegacyMode());
1134 
1135     Typeface typeface = resources.getFont(R.font.vt323_regular);
1136     assertThat(typeface).isNotNull();
1137   }
1138 
1139   @Test
1140   @SdkSuppress(minSdkVersion = O)
1141   @Config(minSdk = O)
getFontFamily()1142   public void getFontFamily() {
1143     // Feature not supported in legacy (raw) resource mode.
1144     assumeFalse(isRobolectricLegacyMode());
1145 
1146     Typeface typeface = resources.getFont(R.font.vt323);
1147     assertThat(typeface).isNotNull();
1148   }
1149 
1150   @Test
1151   @SdkSuppress(minSdkVersion = O)
1152   @Config(minSdk = O)
getFontFamily_downloadable()1153   public void getFontFamily_downloadable() {
1154     // Feature not supported in legacy (raw) resource mode.
1155     assumeFalse(isRobolectricLegacyMode());
1156 
1157     Typeface typeface = resources.getFont(R.font.downloadable);
1158     assertThat(typeface).isNotNull();
1159   }
1160 
1161   @Test
1162   @SdkSuppress(minSdkVersion = Q)
1163   @Config(minSdk = Q)
testFontBuilder()1164   public void testFontBuilder() throws Exception {
1165     // Used to throw `java.io.IOException: Failed to read font contents`
1166     new Font.Builder(context.getResources(), R.font.vt323_regular).build();
1167   }
1168 
1169   @Test
1170   @SdkSuppress(minSdkVersion = Q)
1171   @Config(minSdk = Q)
fontFamily_getFont()1172   public void fontFamily_getFont() throws Exception {
1173     Font platformFont = new Font.Builder(resources, R.font.vt323_regular).build();
1174     FontFamily fontFamily = new FontFamily.Builder(platformFont).build();
1175     assertThat(fontFamily.getFont(0)).isNotNull();
1176   }
1177 
1178   @Test
1179   @SdkSuppress(minSdkVersion = Q)
1180   @Config(minSdk = Q)
getAttributeSetSourceResId()1181   public void getAttributeSetSourceResId() {
1182     XmlResourceParser xmlResourceParser = resources.getXml(R.xml.preferences);
1183 
1184     int sourceResId = Resources.getAttributeSetSourceResId(xmlResourceParser);
1185 
1186     assertThat(sourceResId).isEqualTo(R.xml.preferences);
1187   }
1188 
findRootTag(XmlResourceParser parser)1189   private static String findRootTag(XmlResourceParser parser) throws Exception {
1190     int event;
1191     do {
1192       event = parser.next();
1193     } while (event != XmlPullParser.START_TAG);
1194     return parser.getName();
1195   }
1196 
1197   private static class SubClassResources extends Resources {
SubClassResources(Resources res)1198     public SubClassResources(Resources res) {
1199       super(res.getAssets(), res.getDisplayMetrics(), res.getConfiguration());
1200     }
1201   }
1202 
isRobolectricLegacyMode()1203   private static boolean isRobolectricLegacyMode() {
1204     try {
1205       Class<?> runtimeEnvironmentClass = Class.forName("org.robolectric.RuntimeEnvironment");
1206       Method useLegacyResourcesMethod =
1207           runtimeEnvironmentClass.getDeclaredMethod("useLegacyResources");
1208       return (boolean) useLegacyResourcesMethod.invoke(null);
1209     } catch (Exception e) {
1210       return false;
1211     }
1212   }
1213 }
1214