1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.processor.view.inspector.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotEquals;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.R;
26 import android.content.res.Resources;
27 import android.graphics.Color;
28 import android.view.inspector.InspectableProperty;
29 import android.view.inspector.InspectableProperty.EnumEntry;
30 import android.view.inspector.InspectableProperty.ValueType;
31 import android.view.inspector.InspectionCompanion;
32 
33 import androidx.annotation.AnimRes;
34 import androidx.annotation.AnimatorRes;
35 import androidx.annotation.AnyRes;
36 import androidx.annotation.ArrayRes;
37 import androidx.annotation.BoolRes;
38 import androidx.annotation.ColorInt;
39 import androidx.annotation.ColorLong;
40 import androidx.annotation.DimenRes;
41 import androidx.annotation.DrawableRes;
42 import androidx.annotation.FontRes;
43 import androidx.annotation.IdRes;
44 import androidx.annotation.IntegerRes;
45 import androidx.annotation.InterpolatorRes;
46 import androidx.annotation.LayoutRes;
47 import androidx.annotation.MenuRes;
48 import androidx.annotation.NavigationRes;
49 import androidx.annotation.PluralsRes;
50 import androidx.annotation.RawRes;
51 import androidx.annotation.StringRes;
52 import androidx.annotation.StyleRes;
53 import androidx.annotation.StyleableRes;
54 import androidx.annotation.TransitionRes;
55 import androidx.annotation.XmlRes;
56 import androidx.test.filters.SmallTest;
57 import androidx.test.runner.AndroidJUnit4;
58 
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 
63 import java.util.HashSet;
64 import java.util.Objects;
65 import java.util.Random;
66 import java.util.Set;
67 
68 /**
69  * Behavioral tests for {@link android.processor.view.inspector.PlatformInspectableProcessor}.
70  */
71 @SmallTest
72 @RunWith(AndroidJUnit4.class)
73 public class PlatformInspectableProcessorTest {
74     private Random mRandom;
75     private TestPropertyMapper mPropertyMapper;
76     private TestPropertyReader mPropertyReader;
77 
78     @Before
setup()79     public void setup() {
80         mRandom = new Random();
81         mPropertyMapper = new TestPropertyMapper();
82         mPropertyReader = new TestPropertyReader(mPropertyMapper);
83     }
84 
85     class IntPropertyTest {
86         private final int mValue;
87 
IntPropertyTest(Random seed)88         IntPropertyTest(Random seed) {
89             mValue = seed.nextInt();
90         }
91 
92         @InspectableProperty
getValue()93         public int getValue() {
94             return mValue;
95         }
96     }
97 
98     @Test
testMapAndReadInt()99     public void testMapAndReadInt() {
100         IntPropertyTest node = new IntPropertyTest(mRandom);
101         mapAndRead(node);
102         assertEquals(node.getValue(), mPropertyReader.get("value"));
103     }
104 
105     @Test
testInferredAttributeId()106     public void testInferredAttributeId() {
107         loadCompanion(IntPropertyTest.class).mapProperties(mPropertyMapper);
108         assertEquals(R.attr.value, mPropertyMapper.getAttributeId("value"));
109     }
110 
111     @Test(expected = InspectionCompanion.UninitializedPropertyMapException.class)
testUninitializedPropertyMap()112     public void testUninitializedPropertyMap() {
113         IntPropertyTest node = new IntPropertyTest(mRandom);
114         loadCompanion(IntPropertyTest.class).readProperties(node, mPropertyReader);
115     }
116 
117     class NamedPropertyTest {
118         private final int mValue;
119 
NamedPropertyTest(Random seed)120         NamedPropertyTest(Random seed) {
121             mValue = seed.nextInt();
122         }
123 
124         @InspectableProperty(name = "myNamedValue", hasAttributeId = false)
getValue()125         public int getValue() {
126             return mValue;
127         }
128     }
129 
130     @Test
testNamedProperty()131     public void testNamedProperty() {
132         NamedPropertyTest node = new NamedPropertyTest(mRandom);
133         mapAndRead(node);
134         assertEquals(0, mPropertyMapper.getId("value"));
135         assertEquals(node.getValue(), mPropertyReader.get("myNamedValue"));
136     }
137 
138     class HasAttributeIdFalseTest {
139         @InspectableProperty(hasAttributeId = false)
getValue()140         public int getValue() {
141             return 0;
142         }
143     }
144 
145     @Test
testHasAttributeIdFalse()146     public void testHasAttributeIdFalse() {
147         loadCompanion(HasAttributeIdFalseTest.class).mapProperties(mPropertyMapper);
148         assertEquals(Resources.ID_NULL, mPropertyMapper.getAttributeId("value"));
149     }
150 
151     class AttributeIdEqualsTest {
152         @InspectableProperty(attributeId = 0xdecafbad)
getValue()153         public int getValue() {
154             return 0;
155         }
156     }
157 
158     @Test
testAttributeIdEquals()159     public void testAttributeIdEquals() {
160         loadCompanion(AttributeIdEqualsTest.class).mapProperties(mPropertyMapper);
161         assertEquals(0xdecafbad, mPropertyMapper.getAttributeId("value"));
162     }
163 
164     class InferredPropertyNameTest {
165         private final int mValueA;
166         private final int mValueB;
167         private final int mValueC;
168 
InferredPropertyNameTest(Random seed)169         InferredPropertyNameTest(Random seed) {
170             mValueA = seed.nextInt();
171             mValueB = seed.nextInt();
172             mValueC = seed.nextInt();
173         }
174 
175         @InspectableProperty(hasAttributeId = false)
getValueA()176         public int getValueA() {
177             return mValueA;
178         }
179 
180         @InspectableProperty(hasAttributeId = false)
isValueB()181         public int isValueB() {
182             return mValueB;
183         }
184 
185         @InspectableProperty(hasAttributeId = false)
obtainValueC()186         public int obtainValueC() {
187             return mValueC;
188         }
189     }
190 
191     @Test
testInferredPropertyName()192     public void testInferredPropertyName() {
193         InferredPropertyNameTest node = new InferredPropertyNameTest(mRandom);
194         mapAndRead(node);
195         assertEquals(node.getValueA(), mPropertyReader.get("valueA"));
196         assertEquals(node.isValueB(), mPropertyReader.get("isValueB"));
197         assertEquals(node.obtainValueC(), mPropertyReader.get("obtainValueC"));
198     }
199 
200     class InferredBooleanNameTest {
201         private final boolean mValueA;
202         private final boolean mValueB;
203         private final boolean mValueC;
204 
InferredBooleanNameTest(Random seed)205         InferredBooleanNameTest(Random seed) {
206             mValueA = seed.nextBoolean();
207             mValueB = seed.nextBoolean();
208             mValueC = seed.nextBoolean();
209         }
210 
211         @InspectableProperty(hasAttributeId = false)
getValueA()212         public boolean getValueA() {
213             return mValueA;
214         }
215 
216         @InspectableProperty(hasAttributeId = false)
isValueB()217         public boolean isValueB() {
218             return mValueB;
219         }
220 
221         @InspectableProperty(hasAttributeId = false)
obtainValueC()222         public boolean obtainValueC() {
223             return mValueC;
224         }
225     }
226 
227     @Test
testInferredBooleanName()228     public void testInferredBooleanName() {
229         InferredBooleanNameTest node = new InferredBooleanNameTest(mRandom);
230         mapAndRead(node);
231         assertEquals(node.getValueA(), mPropertyReader.get("valueA"));
232         assertEquals(node.isValueB(), mPropertyReader.get("valueB"));
233         assertEquals(node.obtainValueC(), mPropertyReader.get("obtainValueC"));
234     }
235 
236     class ColorTest {
237         private final int mColorInt;
238         private final long mColorLong;
239 
240         private final Color mColorObject;
241 
ColorTest(Random seed)242         ColorTest(Random seed) {
243             mColorInt = seed.nextInt();
244             mColorLong = Color.pack(seed.nextInt());
245             mColorObject = Color.valueOf(seed.nextInt());
246         }
247 
248         @InspectableProperty(hasAttributeId = false)
249         @ColorInt
getColorInt()250         public int getColorInt() {
251             return mColorInt;
252         }
253 
254         @InspectableProperty(hasAttributeId = false)
255         @ColorLong
getColorLong()256         public long getColorLong() {
257             return mColorLong;
258         }
259 
260         @InspectableProperty(hasAttributeId = false)
getColorObject()261         public Color getColorObject() {
262             return mColorObject;
263         }
264     }
265 
266     @Test
testColorTypeInference()267     public void testColorTypeInference() {
268         ColorTest node = new ColorTest(mRandom);
269         mapAndRead(node);
270         assertEquals(node.getColorInt(), mPropertyReader.get("colorInt"));
271         assertEquals(node.getColorLong(), mPropertyReader.get("colorLong"));
272         assertEquals(node.getColorObject(), mPropertyReader.get("colorObject"));
273         assertEquals(ValueType.COLOR, mPropertyMapper.getValueType("colorInt"));
274         assertEquals(ValueType.COLOR, mPropertyMapper.getValueType("colorLong"));
275         assertEquals(ValueType.COLOR, mPropertyMapper.getValueType("colorObject"));
276     }
277 
278     class ValueTypeTest {
279         private final int mColor;
280         private final int mGravity;
281         private final int mValue;
282 
ValueTypeTest(Random seed)283         ValueTypeTest(Random seed) {
284             mColor = seed.nextInt();
285             mGravity = seed.nextInt();
286             mValue = seed.nextInt();
287         }
288 
289         @InspectableProperty(valueType = ValueType.COLOR)
getColor()290         public int getColor() {
291             return mColor;
292         }
293 
294         @InspectableProperty(valueType = ValueType.GRAVITY)
getGravity()295         public int getGravity() {
296             return mGravity;
297         }
298 
299         @InspectableProperty(valueType = ValueType.NONE)
300         @ColorInt
getValue()301         public int getValue() {
302             return mValue;
303         }
304     }
305 
306     @Test
testValueTypeEquals()307     public void testValueTypeEquals() {
308         ValueTypeTest node = new ValueTypeTest(mRandom);
309         mapAndRead(node);
310         assertEquals(node.getColor(), mPropertyReader.get("color"));
311         assertEquals(node.getGravity(), mPropertyReader.get("gravity"));
312         assertEquals(node.getValue(), mPropertyReader.get("value"));
313         assertEquals(ValueType.COLOR, mPropertyMapper.getValueType("color"));
314         assertEquals(ValueType.GRAVITY, mPropertyMapper.getValueType("gravity"));
315         assertEquals(ValueType.NONE, mPropertyMapper.getValueType("value"));
316     }
317 
318     class PrimitivePropertiesTest {
319         private final boolean mBoolean;
320         private final byte mByte;
321         private final char mChar;
322         private final double mDouble;
323         private final float mFloat;
324         private final int mInt;
325         private final long mLong;
326         private final short mShort;
327 
PrimitivePropertiesTest(Random seed)328         PrimitivePropertiesTest(Random seed) {
329             mBoolean = seed.nextBoolean();
330             mByte = (byte) seed.nextInt();
331             mChar = randomLetter(seed);
332             mDouble = seed.nextDouble();
333             mFloat = seed.nextFloat();
334             mInt = seed.nextInt();
335             mLong = seed.nextLong();
336             mShort = (short) seed.nextInt();
337         }
338 
339         @InspectableProperty(hasAttributeId = false)
getBoolean()340         public boolean getBoolean() {
341             return mBoolean;
342         }
343 
344         @InspectableProperty(hasAttributeId = false)
getByte()345         public byte getByte() {
346             return mByte;
347         }
348 
349         @InspectableProperty(hasAttributeId = false)
getChar()350         public char getChar() {
351             return mChar;
352         }
353 
354         @InspectableProperty(hasAttributeId = false)
getDouble()355         public double getDouble() {
356             return mDouble;
357         }
358 
359         @InspectableProperty(hasAttributeId = false)
getFloat()360         public float getFloat() {
361             return mFloat;
362         }
363 
364         @InspectableProperty(hasAttributeId = false)
getInt()365         public int getInt() {
366             return mInt;
367         }
368 
369         @InspectableProperty(hasAttributeId = false)
getLong()370         public long getLong() {
371             return mLong;
372         }
373 
374         @InspectableProperty(hasAttributeId = false)
getShort()375         public short getShort() {
376             return mShort;
377         }
378     }
379 
380     @Test
testPrimitiveProperties()381     public void testPrimitiveProperties() {
382         PrimitivePropertiesTest node = new PrimitivePropertiesTest(mRandom);
383         mapAndRead(node);
384         assertEquals(node.getBoolean(), mPropertyReader.get("boolean"));
385         assertEquals(node.getByte(), mPropertyReader.get("byte"));
386         assertEquals(node.getChar(), mPropertyReader.get("char"));
387         assertEquals(node.getDouble(), mPropertyReader.get("double"));
388         assertEquals(node.getFloat(), mPropertyReader.get("float"));
389         assertEquals(node.getInt(), mPropertyReader.get("int"));
390         assertEquals(node.getLong(), mPropertyReader.get("long"));
391         assertEquals(node.getShort(), mPropertyReader.get("short"));
392     }
393 
394     class ObjectPropertiesTest {
395         private final String mText;
396 
ObjectPropertiesTest(Random seed)397         ObjectPropertiesTest(Random seed) {
398             final StringBuilder stringBuilder = new StringBuilder();
399             final int length = seed.nextInt(8) + 8;
400 
401             for (int i = 0; i < length; i++) {
402                 stringBuilder.append(randomLetter(seed));
403             }
404 
405             mText = stringBuilder.toString();
406         }
407 
408         @InspectableProperty
getText()409         public String getText() {
410             return mText;
411         }
412 
413         @InspectableProperty(hasAttributeId = false)
getNull()414         public Objects getNull() {
415             return null;
416         }
417     }
418 
419     @Test
testObjectProperties()420     public void testObjectProperties() {
421         ObjectPropertiesTest node = new ObjectPropertiesTest(mRandom);
422         mapAndRead(node);
423         assertEquals(node.getText(), mPropertyReader.get("text"));
424         assertNull(mPropertyReader.get("null"));
425         assertNotEquals(0, mPropertyMapper.getId("null"));
426     }
427 
428     class IntEnumTest {
429         private int mValue;
430 
431         @InspectableProperty(enumMapping = {
432                 @InspectableProperty.EnumEntry(name = "ONE", value = 1),
433                 @EnumEntry(name = "TWO", value = 2)})
getValue()434         public int getValue() {
435             return mValue;
436         }
437 
setValue(int value)438         public void setValue(int value) {
439             mValue = value;
440         }
441     }
442 
443     @Test
testIntEnum()444     public void testIntEnum() {
445         IntEnumTest node = new IntEnumTest();
446         InspectionCompanion<IntEnumTest> companion = loadCompanion(IntEnumTest.class);
447         companion.mapProperties(mPropertyMapper);
448 
449         node.setValue(1);
450         companion.readProperties(node, mPropertyReader);
451         assertEquals("ONE", mPropertyReader.getIntEnum("value"));
452 
453         node.setValue(2);
454         companion.readProperties(node, mPropertyReader);
455         assertEquals("TWO", mPropertyReader.getIntEnum("value"));
456 
457         node.setValue(3);
458         companion.readProperties(node, mPropertyReader);
459         assertNull(mPropertyReader.getIntEnum("value"));
460     }
461 
462     class IntFlagTest {
463         private int mValue;
464 
465         @InspectableProperty(flagMapping = {
466                 @InspectableProperty.FlagEntry(name = "ONE", target = 0x1, mask = 0x3),
467                 @InspectableProperty.FlagEntry(name = "TWO", target = 0x2, mask = 0x3),
468                 @InspectableProperty.FlagEntry(name = "THREE", target = 0x3, mask = 0x3),
469                 @InspectableProperty.FlagEntry(name = "FOUR", target = 0x4)})
getValue()470         public int getValue() {
471             return mValue;
472         }
473 
setValue(int value)474         public void setValue(int value) {
475             mValue = value;
476         }
477     }
478 
479     @Test
testIntFlag()480     public void testIntFlag() {
481         IntFlagTest node = new IntFlagTest();
482         InspectionCompanion<IntFlagTest> companion = loadCompanion(IntFlagTest.class);
483         companion.mapProperties(mPropertyMapper);
484 
485         node.setValue(0);
486         companion.readProperties(node, mPropertyReader);
487         assertTrue(mPropertyReader.getIntFlag("value").isEmpty());
488 
489         node.setValue(1);
490         companion.readProperties(node, mPropertyReader);
491         assertEquals(setOf("ONE"), mPropertyReader.getIntFlag("value"));
492 
493         node.setValue(2);
494         companion.readProperties(node, mPropertyReader);
495         assertEquals(setOf("TWO"), mPropertyReader.getIntFlag("value"));
496 
497         node.setValue(3);
498         companion.readProperties(node, mPropertyReader);
499         assertEquals(setOf("THREE"), mPropertyReader.getIntFlag("value"));
500 
501         node.setValue(4);
502         companion.readProperties(node, mPropertyReader);
503         assertEquals(setOf("FOUR"), mPropertyReader.getIntFlag("value"));
504 
505         node.setValue(5);
506         companion.readProperties(node, mPropertyReader);
507         assertEquals(setOf("FOUR", "ONE"), mPropertyReader.getIntFlag("value"));
508     }
509 
510     class PublicFieldTest {
511         @InspectableProperty
512         public final int value;
513 
PublicFieldTest(Random seed)514         PublicFieldTest(Random seed) {
515             value = seed.nextInt();
516         }
517     }
518 
519     @Test
testPublicField()520     public void testPublicField() {
521         PublicFieldTest node = new PublicFieldTest(mRandom);
522         mapAndRead(node);
523         assertEquals(node.value, mPropertyReader.get("value"));
524     }
525 
526     class ResourceIdTest {
527         @AnimatorRes private final int mAnimatorId;
528         @AnimRes private final int mAnimId;
529         @AnyRes private final int mAnyId;
530         @ArrayRes private final int mArrayId;
531         @BoolRes private final int mBoolId;
532         @DimenRes private final int mDimenId;
533         @DrawableRes private final int mDrawableId;
534         @FontRes private final int mFontId;
535         @IdRes private final int mIdId;
536         @IntegerRes private final int mIntegerId;
537         @InterpolatorRes private final int mInterpolatorId;
538         @LayoutRes private final int mLayoutId;
539         @MenuRes private final int mMenuId;
540         @NavigationRes private final int mNavigationId;
541         @PluralsRes private final int mPluralsId;
542         @RawRes private final int mRawId;
543         @StringRes private final int mStringId;
544         @StyleableRes private final int mStyleableId;
545         @StyleRes private final int mStyleId;
546         @TransitionRes private final int mTransitionId;
547         @XmlRes private final int mXmlId;
548         private final int mUnannotatedId;
549 
ResourceIdTest(Random seed)550         ResourceIdTest(Random seed) {
551             mAnimatorId = seed.nextInt();
552             mAnimId = seed.nextInt();
553             mAnyId = seed.nextInt();
554             mArrayId = seed.nextInt();
555             mBoolId = seed.nextInt();
556             mDimenId = seed.nextInt();
557             mDrawableId = seed.nextInt();
558             mFontId = seed.nextInt();
559             mIdId = seed.nextInt();
560             mIntegerId = seed.nextInt();
561             mInterpolatorId = seed.nextInt();
562             mLayoutId = seed.nextInt();
563             mMenuId = seed.nextInt();
564             mNavigationId = seed.nextInt();
565             mPluralsId = seed.nextInt();
566             mRawId = seed.nextInt();
567             mStringId = seed.nextInt();
568             mStyleableId = seed.nextInt();
569             mStyleId = seed.nextInt();
570             mTransitionId = seed.nextInt();
571             mXmlId = seed.nextInt();
572             mUnannotatedId = seed.nextInt();
573         }
574 
575         @InspectableProperty(hasAttributeId = false)
576         @AnimatorRes
getAnimatorId()577         public int getAnimatorId() {
578             return mAnimatorId;
579         }
580 
581         @InspectableProperty(hasAttributeId = false)
582         @AnimRes
getAnimId()583         public int getAnimId() {
584             return mAnimId;
585         }
586 
587         @InspectableProperty(hasAttributeId = false)
588         @AnyRes
getAnyId()589         public int getAnyId() {
590             return mAnimId;
591         }
592 
593         @InspectableProperty(hasAttributeId = false)
594         @ArrayRes
getArrayId()595         public int getArrayId() {
596             return mArrayId;
597         }
598 
599         @InspectableProperty(hasAttributeId = false)
600         @BoolRes
getBoolId()601         public int getBoolId() {
602             return mBoolId;
603         }
604 
605         @InspectableProperty(hasAttributeId = false)
606         @DimenRes
getDimenId()607         public int getDimenId() {
608             return mDimenId;
609         }
610 
611         @InspectableProperty(hasAttributeId = false)
612         @DrawableRes
getDrawableId()613         public int getDrawableId() {
614             return mDrawableId;
615         }
616 
617         @InspectableProperty(hasAttributeId = false)
618         @FontRes
getFontId()619         public int getFontId() {
620             return mFontId;
621         }
622 
623         @InspectableProperty(hasAttributeId = false)
624         @IdRes
getIdId()625         public int getIdId() {
626             return mIdId;
627         }
628 
629         @InspectableProperty(hasAttributeId = false)
630         @IntegerRes
getIntegerId()631         public int getIntegerId() {
632             return mIntegerId;
633         }
634 
635         @InspectableProperty(hasAttributeId = false)
636         @InterpolatorRes
getInterpolatorId()637         public int getInterpolatorId() {
638             return mInterpolatorId;
639         }
640 
641         @InspectableProperty(hasAttributeId = false)
642         @LayoutRes
getLayoutId()643         public int getLayoutId() {
644             return mLayoutId;
645         }
646 
647         @InspectableProperty(hasAttributeId = false)
648         @MenuRes
getMenuId()649         public int getMenuId() {
650             return mMenuId;
651         }
652 
653         @InspectableProperty(hasAttributeId = false)
654         @NavigationRes
getNavigationId()655         public int getNavigationId() {
656             return mNavigationId;
657         }
658 
659         @InspectableProperty(hasAttributeId = false)
660         @PluralsRes
getPluralsId()661         public int getPluralsId() {
662             return mPluralsId;
663         }
664 
665         @InspectableProperty(hasAttributeId = false)
666         @RawRes
getRawId()667         public int getRawId() {
668             return mRawId;
669         }
670 
671         @InspectableProperty(hasAttributeId = false)
672         @StringRes
getStringId()673         public int getStringId() {
674             return mStringId;
675         }
676 
677         @InspectableProperty(hasAttributeId = false)
678         @StyleableRes
getStyleableId()679         public int getStyleableId() {
680             return mStyleableId;
681         }
682 
683         @InspectableProperty(hasAttributeId = false)
684         @StyleRes
getStyleId()685         public int getStyleId() {
686             return mStyleId;
687         }
688 
689         @InspectableProperty(hasAttributeId = false)
690         @TransitionRes
getTransitionId()691         public int getTransitionId() {
692             return mTransitionId;
693         }
694 
695         @InspectableProperty(hasAttributeId = false)
696         @XmlRes
getXmlId()697         public int getXmlId() {
698             return mXmlId;
699         }
700 
701         @InspectableProperty(hasAttributeId = false, valueType = ValueType.RESOURCE_ID)
getUnannotatedId()702         public int getUnannotatedId() {
703             return mUnannotatedId;
704         }
705     }
706 
707     @Test
testResourceId()708     public void testResourceId() {
709         ResourceIdTest node = new ResourceIdTest(mRandom);
710         mapAndRead(node);
711 
712         assertEquals(node.getAnimatorId(), mPropertyReader.get("animatorId"));
713         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("animatorId"));
714 
715         assertEquals(node.getAnimId(), mPropertyReader.get("animId"));
716         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("animId"));
717 
718         assertEquals(node.getAnyId(), mPropertyReader.get("anyId"));
719         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("anyId"));
720 
721         assertEquals(node.getArrayId(), mPropertyReader.get("arrayId"));
722         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("arrayId"));
723 
724         assertEquals(node.getBoolId(), mPropertyReader.get("boolId"));
725         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("boolId"));
726 
727         assertEquals(node.getDimenId(), mPropertyReader.get("dimenId"));
728         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("dimenId"));
729 
730         assertEquals(node.getDrawableId(), mPropertyReader.get("drawableId"));
731         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("drawableId"));
732 
733         assertEquals(node.getFontId(), mPropertyReader.get("fontId"));
734         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("fontId"));
735 
736         assertEquals(node.getIdId(), mPropertyReader.get("idId"));
737         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("idId"));
738 
739         assertEquals(node.getIntegerId(), mPropertyReader.get("integerId"));
740         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("integerId"));
741 
742         assertEquals(node.getInterpolatorId(), mPropertyReader.get("interpolatorId"));
743         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("interpolatorId"));
744 
745         assertEquals(node.getLayoutId(), mPropertyReader.get("layoutId"));
746         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("layoutId"));
747 
748         assertEquals(node.getMenuId(), mPropertyReader.get("menuId"));
749         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("menuId"));
750 
751         assertEquals(node.getNavigationId(), mPropertyReader.get("navigationId"));
752         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("navigationId"));
753 
754         assertEquals(node.getPluralsId(), mPropertyReader.get("pluralsId"));
755         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("pluralsId"));
756 
757         assertEquals(node.getRawId(), mPropertyReader.get("rawId"));
758         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("rawId"));
759 
760         assertEquals(node.getStringId(), mPropertyReader.get("stringId"));
761         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("stringId"));
762 
763         assertEquals(node.getStyleableId(), mPropertyReader.get("styleableId"));
764         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("styleableId"));
765 
766         assertEquals(node.getStyleId(), mPropertyReader.get("styleId"));
767         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("styleId"));
768 
769         assertEquals(node.getTransitionId(), mPropertyReader.get("transitionId"));
770         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("transitionId"));
771 
772         assertEquals(node.getXmlId(), mPropertyReader.get("xmlId"));
773         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("xmlId"));
774 
775         assertEquals(node.getUnannotatedId(), mPropertyReader.get("unannotatedId"));
776         assertEquals(ValueType.RESOURCE_ID, mPropertyMapper.getValueType("unannotatedId"));
777     }
778 
setOf(T... items)779     private static <T> Set<T> setOf(T... items) {
780         Set<T> set = new HashSet<>(items.length);
781 
782         for (T item : items) {
783             set.add(item);
784         }
785 
786         return set;
787     }
788 
789     @SuppressWarnings("unchecked")
mapAndRead(T node)790     private <T> void mapAndRead(T node) {
791         InspectionCompanion<T> companion = loadCompanion((Class<T>) node.getClass());
792         companion.mapProperties(mPropertyMapper);
793         companion.readProperties(node, mPropertyReader);
794     }
795 
796     @SuppressWarnings("unchecked")
loadCompanion(Class<T> cls)797     private <T> InspectionCompanion<T> loadCompanion(Class<T> cls) {
798         final ClassLoader classLoader = cls.getClassLoader();
799         final String companionName = String.format("%s$InspectionCompanion", cls.getName());
800 
801         try {
802             final Class<InspectionCompanion<T>> companion =
803                     (Class<InspectionCompanion<T>>) classLoader.loadClass(companionName);
804             return companion.newInstance();
805         } catch (ClassNotFoundException e) {
806             fail(String.format("Unable to load companion for %s", cls.getCanonicalName()));
807         } catch (InstantiationException | IllegalAccessException e) {
808             fail(String.format("Unable to instantiate companion for %s", cls.getCanonicalName()));
809         }
810 
811         return null;
812     }
813 
randomLetter(Random random)814     private char randomLetter(Random random) {
815         final String alphabet = "abcdefghijklmnopqrstuvwxyz";
816         return alphabet.charAt(random.nextInt(alphabet.length()));
817     }
818 }
819