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