Home | History | Annotate | Download | only in cts
      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
     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 
     88         IntPropertyTest(Random seed) {
     89             mValue = seed.nextInt();
     90         }
     91 
     92         @InspectableProperty
     93         public int getValue() {
     94             return mValue;
     95         }
     96     }
     97 
     98     @Test
     99     public void testMapAndReadInt() {
    100         IntPropertyTest node = new IntPropertyTest(mRandom);
    101         mapAndRead(node);
    102         assertEquals(node.getValue(), mPropertyReader.get("value"));
    103     }
    104 
    105     @Test
    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)
    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 
    120         NamedPropertyTest(Random seed) {
    121             mValue = seed.nextInt();
    122         }
    123 
    124         @InspectableProperty(name = "myNamedValue", hasAttributeId = false)
    125         public int getValue() {
    126             return mValue;
    127         }
    128     }
    129 
    130     @Test
    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)
    140         public int getValue() {
    141             return 0;
    142         }
    143     }
    144 
    145     @Test
    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)
    153         public int getValue() {
    154             return 0;
    155         }
    156     }
    157 
    158     @Test
    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 
    169         InferredPropertyNameTest(Random seed) {
    170             mValueA = seed.nextInt();
    171             mValueB = seed.nextInt();
    172             mValueC = seed.nextInt();
    173         }
    174 
    175         @InspectableProperty(hasAttributeId = false)
    176         public int getValueA() {
    177             return mValueA;
    178         }
    179 
    180         @InspectableProperty(hasAttributeId = false)
    181         public int isValueB() {
    182             return mValueB;
    183         }
    184 
    185         @InspectableProperty(hasAttributeId = false)
    186         public int obtainValueC() {
    187             return mValueC;
    188         }
    189     }
    190 
    191     @Test
    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 
    205         InferredBooleanNameTest(Random seed) {
    206             mValueA = seed.nextBoolean();
    207             mValueB = seed.nextBoolean();
    208             mValueC = seed.nextBoolean();
    209         }
    210 
    211         @InspectableProperty(hasAttributeId = false)
    212         public boolean getValueA() {
    213             return mValueA;
    214         }
    215 
    216         @InspectableProperty(hasAttributeId = false)
    217         public boolean isValueB() {
    218             return mValueB;
    219         }
    220 
    221         @InspectableProperty(hasAttributeId = false)
    222         public boolean obtainValueC() {
    223             return mValueC;
    224         }
    225     }
    226 
    227     @Test
    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 
    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
    250         public int getColorInt() {
    251             return mColorInt;
    252         }
    253 
    254         @InspectableProperty(hasAttributeId = false)
    255         @ColorLong
    256         public long getColorLong() {
    257             return mColorLong;
    258         }
    259 
    260         @InspectableProperty(hasAttributeId = false)
    261         public Color getColorObject() {
    262             return mColorObject;
    263         }
    264     }
    265 
    266     @Test
    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 
    283         ValueTypeTest(Random seed) {
    284             mColor = seed.nextInt();
    285             mGravity = seed.nextInt();
    286             mValue = seed.nextInt();
    287         }
    288 
    289         @InspectableProperty(valueType = ValueType.COLOR)
    290         public int getColor() {
    291             return mColor;
    292         }
    293 
    294         @InspectableProperty(valueType = ValueType.GRAVITY)
    295         public int getGravity() {
    296             return mGravity;
    297         }
    298 
    299         @InspectableProperty(valueType = ValueType.NONE)
    300         @ColorInt
    301         public int getValue() {
    302             return mValue;
    303         }
    304     }
    305 
    306     @Test
    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 
    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)
    340         public boolean getBoolean() {
    341             return mBoolean;
    342         }
    343 
    344         @InspectableProperty(hasAttributeId = false)
    345         public byte getByte() {
    346             return mByte;
    347         }
    348 
    349         @InspectableProperty(hasAttributeId = false)
    350         public char getChar() {
    351             return mChar;
    352         }
    353 
    354         @InspectableProperty(hasAttributeId = false)
    355         public double getDouble() {
    356             return mDouble;
    357         }
    358 
    359         @InspectableProperty(hasAttributeId = false)
    360         public float getFloat() {
    361             return mFloat;
    362         }
    363 
    364         @InspectableProperty(hasAttributeId = false)
    365         public int getInt() {
    366             return mInt;
    367         }
    368 
    369         @InspectableProperty(hasAttributeId = false)
    370         public long getLong() {
    371             return mLong;
    372         }
    373 
    374         @InspectableProperty(hasAttributeId = false)
    375         public short getShort() {
    376             return mShort;
    377         }
    378     }
    379 
    380     @Test
    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 
    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
    409         public String getText() {
    410             return mText;
    411         }
    412 
    413         @InspectableProperty(hasAttributeId = false)
    414         public Objects getNull() {
    415             return null;
    416         }
    417     }
    418 
    419     @Test
    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)})
    434         public int getValue() {
    435             return mValue;
    436         }
    437 
    438         public void setValue(int value) {
    439             mValue = value;
    440         }
    441     }
    442 
    443     @Test
    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)})
    470         public int getValue() {
    471             return mValue;
    472         }
    473 
    474         public void setValue(int value) {
    475             mValue = value;
    476         }
    477     }
    478 
    479     @Test
    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 
    514         PublicFieldTest(Random seed) {
    515             value = seed.nextInt();
    516         }
    517     }
    518 
    519     @Test
    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 
    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
    577         public int getAnimatorId() {
    578             return mAnimatorId;
    579         }
    580 
    581         @InspectableProperty(hasAttributeId = false)
    582         @AnimRes
    583         public int getAnimId() {
    584             return mAnimId;
    585         }
    586 
    587         @InspectableProperty(hasAttributeId = false)
    588         @AnyRes
    589         public int getAnyId() {
    590             return mAnimId;
    591         }
    592 
    593         @InspectableProperty(hasAttributeId = false)
    594         @ArrayRes
    595         public int getArrayId() {
    596             return mArrayId;
    597         }
    598 
    599         @InspectableProperty(hasAttributeId = false)
    600         @BoolRes
    601         public int getBoolId() {
    602             return mBoolId;
    603         }
    604 
    605         @InspectableProperty(hasAttributeId = false)
    606         @DimenRes
    607         public int getDimenId() {
    608             return mDimenId;
    609         }
    610 
    611         @InspectableProperty(hasAttributeId = false)
    612         @DrawableRes
    613         public int getDrawableId() {
    614             return mDrawableId;
    615         }
    616 
    617         @InspectableProperty(hasAttributeId = false)
    618         @FontRes
    619         public int getFontId() {
    620             return mFontId;
    621         }
    622 
    623         @InspectableProperty(hasAttributeId = false)
    624         @IdRes
    625         public int getIdId() {
    626             return mIdId;
    627         }
    628 
    629         @InspectableProperty(hasAttributeId = false)
    630         @IntegerRes
    631         public int getIntegerId() {
    632             return mIntegerId;
    633         }
    634 
    635         @InspectableProperty(hasAttributeId = false)
    636         @InterpolatorRes
    637         public int getInterpolatorId() {
    638             return mInterpolatorId;
    639         }
    640 
    641         @InspectableProperty(hasAttributeId = false)
    642         @LayoutRes
    643         public int getLayoutId() {
    644             return mLayoutId;
    645         }
    646 
    647         @InspectableProperty(hasAttributeId = false)
    648         @MenuRes
    649         public int getMenuId() {
    650             return mMenuId;
    651         }
    652 
    653         @InspectableProperty(hasAttributeId = false)
    654         @NavigationRes
    655         public int getNavigationId() {
    656             return mNavigationId;
    657         }
    658 
    659         @InspectableProperty(hasAttributeId = false)
    660         @PluralsRes
    661         public int getPluralsId() {
    662             return mPluralsId;
    663         }
    664 
    665         @InspectableProperty(hasAttributeId = false)
    666         @RawRes
    667         public int getRawId() {
    668             return mRawId;
    669         }
    670 
    671         @InspectableProperty(hasAttributeId = false)
    672         @StringRes
    673         public int getStringId() {
    674             return mStringId;
    675         }
    676 
    677         @InspectableProperty(hasAttributeId = false)
    678         @StyleableRes
    679         public int getStyleableId() {
    680             return mStyleableId;
    681         }
    682 
    683         @InspectableProperty(hasAttributeId = false)
    684         @StyleRes
    685         public int getStyleId() {
    686             return mStyleId;
    687         }
    688 
    689         @InspectableProperty(hasAttributeId = false)
    690         @TransitionRes
    691         public int getTransitionId() {
    692             return mTransitionId;
    693         }
    694 
    695         @InspectableProperty(hasAttributeId = false)
    696         @XmlRes
    697         public int getXmlId() {
    698             return mXmlId;
    699         }
    700 
    701         @InspectableProperty(hasAttributeId = false, valueType = ValueType.RESOURCE_ID)
    702         public int getUnannotatedId() {
    703             return mUnannotatedId;
    704         }
    705     }
    706 
    707     @Test
    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 
    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")
    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")
    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 
    814     private char randomLetter(Random random) {
    815         final String alphabet = "abcdefghijklmnopqrstuvwxyz";
    816         return alphabet.charAt(random.nextInt(alphabet.length()));
    817     }
    818 }
    819