Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 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.graphics.drawable.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertNotNull;
     22 import static org.junit.Assert.assertNull;
     23 import static org.junit.Assert.assertSame;
     24 import static org.junit.Assert.assertTrue;
     25 import static org.junit.Assert.fail;
     26 import static org.mockito.Matchers.any;
     27 import static org.mockito.Matchers.anyInt;
     28 import static org.mockito.Matchers.anyLong;
     29 import static org.mockito.Mockito.atLeastOnce;
     30 import static org.mockito.Mockito.doAnswer;
     31 import static org.mockito.Mockito.doNothing;
     32 import static org.mockito.Mockito.doReturn;
     33 import static org.mockito.Mockito.mock;
     34 import static org.mockito.Mockito.never;
     35 import static org.mockito.Mockito.reset;
     36 import static org.mockito.Mockito.spy;
     37 import static org.mockito.Mockito.times;
     38 import static org.mockito.Mockito.verify;
     39 
     40 import android.content.res.ColorStateList;
     41 import android.graphics.Canvas;
     42 import android.graphics.Color;
     43 import android.graphics.ColorFilter;
     44 import android.graphics.PixelFormat;
     45 import android.graphics.PorterDuff;
     46 import android.graphics.PorterDuff.Mode;
     47 import android.graphics.Rect;
     48 import android.graphics.drawable.ColorDrawable;
     49 import android.graphics.drawable.Drawable;
     50 import android.graphics.drawable.DrawableContainer;
     51 import android.graphics.drawable.DrawableContainer.DrawableContainerState;
     52 import android.graphics.drawable.LevelListDrawable;
     53 import android.support.test.filters.SmallTest;
     54 import android.support.test.runner.AndroidJUnit4;
     55 
     56 import org.junit.Before;
     57 import org.junit.Test;
     58 import org.junit.runner.RunWith;
     59 import org.mockito.invocation.InvocationOnMock;
     60 
     61 import java.util.Arrays;
     62 
     63 @SmallTest
     64 @RunWith(AndroidJUnit4.class)
     65 public class DrawableContainerTest {
     66     private DrawableContainerState mDrawableContainerState;
     67 
     68     private MockDrawableContainer mMockDrawableContainer;
     69     private DrawableContainer mDrawableContainer;
     70 
     71     @Before
     72     public void setup() {
     73         // DrawableContainerState has no public constructor. Obtain an instance through
     74         // LevelListDrawable.getConstants(). This is fine for testing the final methods of
     75         // DrawableContainerState.
     76         mDrawableContainerState =
     77             (DrawableContainerState) new LevelListDrawable().getConstantState();
     78         assertNotNull(mDrawableContainerState);
     79 
     80         mMockDrawableContainer = new MockDrawableContainer();
     81         // While the two fields point to the same object, the second one is there to
     82         // workaround the bug in CTS coverage tool that is not recognizing calls on
     83         // subclasses.
     84         mDrawableContainer = mMockDrawableContainer;
     85 
     86         assertNull(mDrawableContainer.getCurrent());
     87     }
     88 
     89     @Test(expected=NullPointerException.class)
     90     public void testConstantStateNotSet() {
     91         // This should throw NPE since our mock container has not been configured with
     92         // constant state yet
     93         mDrawableContainer.getConstantState();
     94     }
     95 
     96     @Test
     97     public void testDraw() {
     98         mDrawableContainer.draw(null);
     99         mDrawableContainer.draw(new Canvas());
    100 
    101         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    102         Drawable dr = spy(new ColorDrawable(Color.WHITE));
    103         addAndSelectDrawable(dr);
    104 
    105         reset(dr);
    106         doNothing().when(dr).draw(any());
    107         mDrawableContainer.draw(null);
    108         verify(dr, times(1)).draw(any());
    109 
    110         reset(dr);
    111         doNothing().when(dr).draw(any());
    112         mDrawableContainer.draw(new Canvas());
    113         verify(dr, times(1)).draw(any());
    114     }
    115 
    116     @Test
    117     public void testSetEnterFadeDuration() {
    118         verifySetEnterFadeDuration(1000);
    119         verifySetEnterFadeDuration(0);
    120     }
    121 
    122     private void verifySetEnterFadeDuration(int enterFadeDuration) {
    123         DrawableContainer container = new LevelListDrawable();
    124         DrawableContainerState cs = ((DrawableContainerState) container.getConstantState());
    125         container.setEnterFadeDuration(enterFadeDuration);
    126         assertEquals(enterFadeDuration, cs.getEnterFadeDuration());
    127     }
    128 
    129     @Test
    130     public void testSetExitFadeDuration() {
    131         verifySetExitFadeDuration(1000);
    132         verifySetExitFadeDuration(0);
    133     }
    134 
    135     private void verifySetExitFadeDuration(int exitFadeDuration) {
    136         DrawableContainer container = new LevelListDrawable();
    137         DrawableContainerState cs = ((DrawableContainerState) container.getConstantState());
    138         container.setExitFadeDuration(exitFadeDuration);
    139         assertEquals(exitFadeDuration, cs.getExitFadeDuration());
    140     }
    141 
    142     @Test(expected=NullPointerException.class)
    143     public void testGetChangingConfigurationsNoConstantState() {
    144         // Should throw NullPointerException if the constant state is not set
    145         mDrawableContainer.getChangingConfigurations();
    146     }
    147 
    148     @Test
    149     public void testGetChangingConfigurations() {
    150         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    151         MockDrawable dr0 = new MockDrawable();
    152         dr0.setChangingConfigurations(0x001);
    153         mDrawableContainerState.addChild(dr0);
    154         MockDrawable dr1 = new MockDrawable();
    155         dr1.setChangingConfigurations(0x010);
    156         mDrawableContainerState.addChild(dr1);
    157         mDrawableContainer.selectDrawable(0);
    158         assertSame(dr0, mDrawableContainer.getCurrent());
    159 
    160         // can not set mDrawableContainerState's ChangingConfigurations
    161         mDrawableContainer.setChangingConfigurations(0x100);
    162         assertEquals(0x111 | mDrawableContainerState.getChangingConfigurations(),
    163                 mDrawableContainer.getChangingConfigurations());
    164     }
    165 
    166     @Test(expected=NullPointerException.class)
    167     public void testGetPaddingNoConstantState() {
    168         Rect result = new Rect(1, 1, 1, 1);
    169         // Should throw NullPointerException if the constant state is not set
    170         mDrawableContainer.getPadding(result);
    171     }
    172 
    173     @Test
    174     public void testGetPadding() {
    175         Rect result = new Rect(1, 1, 1, 1);
    176 
    177         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    178         Drawable dr0 = spy(new ColorDrawable(Color.BLUE));
    179         doAnswer((InvocationOnMock invocation) -> {
    180             Rect target = (Rect) invocation.getArguments() [0];
    181             target.set(1, 2, 0, 0);
    182             return true;
    183         }).when(dr0).getPadding(any());
    184         mDrawableContainerState.addChild(dr0);
    185         Drawable dr1 = spy(new ColorDrawable(Color.RED));
    186         doAnswer((InvocationOnMock invocation) -> {
    187             Rect target = (Rect) invocation.getArguments() [0];
    188             target.set(0, 0, 3, 4);
    189             return true;
    190         }).when(dr1).getPadding(any());
    191         mDrawableContainerState.addChild(dr1);
    192         mDrawableContainer.selectDrawable(0);
    193         assertSame(dr0, mDrawableContainer.getCurrent());
    194 
    195         // use the current drawable's padding
    196         mDrawableContainerState.setVariablePadding(true);
    197         assertNull(mDrawableContainerState.getConstantPadding());
    198         assertTrue(mDrawableContainer.getPadding(result));
    199         assertEquals(new Rect(1, 2, 0, 0), result);
    200 
    201         // use constant state's padding
    202         mDrawableContainerState.setVariablePadding(false);
    203         assertNotNull(mDrawableContainerState.getConstantPadding());
    204         assertTrue(mDrawableContainer.getPadding(result));
    205         assertEquals(mDrawableContainerState.getConstantPadding(), result);
    206 
    207         // use default padding
    208         mDrawableContainer.selectDrawable(-1);
    209         assertNull(mDrawableContainer.getCurrent());
    210         mDrawableContainerState.setVariablePadding(true);
    211         assertNull(mDrawableContainerState.getConstantPadding());
    212         assertFalse(mDrawableContainer.getPadding(result));
    213         assertEquals(new Rect(0, 0, 0, 0), result);
    214 
    215         try {
    216             mDrawableContainer.getPadding(null);
    217             fail("Should throw NullPointerException if the padding is null.");
    218         } catch (NullPointerException e) {
    219         }
    220     }
    221 
    222     @Test
    223     public void testSetAlpha() {
    224         mDrawableContainer.setAlpha(0);
    225 
    226         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    227         Drawable mockDrawable = spy(new ColorDrawable(Color.BLACK));
    228         addAndSelectDrawable(mockDrawable);
    229 
    230         // call current drawable's setAlpha if alpha is changed.
    231         reset(mockDrawable);
    232         mDrawableContainer.setAlpha(1);
    233         verify(mockDrawable, times(1)).setAlpha(1);
    234 
    235         // does not call it if alpha is not changed.
    236         reset(mockDrawable);
    237         mDrawableContainer.setAlpha(1);
    238         verify(mockDrawable, never()).setAlpha(anyInt());
    239     }
    240 
    241     @Test
    242     public void testSetDither() {
    243         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    244         mDrawableContainer.setDither(false);
    245         mDrawableContainer.setDither(true);
    246 
    247         Drawable dr = spy(new ColorDrawable(Color.BLUE));
    248         addAndSelectDrawable(dr);
    249 
    250         // call current drawable's setDither if dither is changed.
    251         reset(dr);
    252         mDrawableContainer.setDither(false);
    253         verify(dr, times(1)).setDither(false);
    254 
    255         // does not call it if dither is not changed.
    256         reset(dr);
    257         mDrawableContainer.setDither(true);
    258         verify(dr, times(1)).setDither(true);
    259     }
    260 
    261     @Test
    262     public void testSetHotspotBounds() {
    263         Rect bounds = new Rect(10, 15, 100, 150);
    264         assertNull(mDrawableContainer.getCurrent());
    265 
    266         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    267 
    268         MockDrawable dr = new MockDrawable();
    269         addAndSelectDrawable(dr);
    270 
    271         dr.reset();
    272         mDrawableContainer.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
    273         Rect outRect = new Rect();
    274         mDrawableContainer.getHotspotBounds(outRect);
    275         assertEquals(bounds, outRect);
    276 
    277         dr.reset();
    278     }
    279 
    280     @Test
    281     public void testGetHotspotBounds() {
    282         Rect bounds = new Rect(10, 15, 100, 150);
    283         assertNull(mDrawableContainer.getCurrent());
    284 
    285         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    286 
    287         MockDrawable dr = new MockDrawable();
    288         addAndSelectDrawable(dr);
    289 
    290         dr.reset();
    291         mDrawableContainer.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
    292         Rect outRect = new Rect();
    293         mDrawableContainer.getHotspotBounds(outRect);
    294         assertEquals(bounds, outRect);
    295 
    296         dr.reset();
    297     }
    298 
    299     @Test
    300     public void testSetColorFilter() {
    301         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    302         mDrawableContainer.setColorFilter(null);
    303         mDrawableContainer.setColorFilter(new ColorFilter());
    304 
    305         Drawable mockDrawable = spy(new ColorDrawable(Color.MAGENTA));
    306         addAndSelectDrawable(mockDrawable);
    307 
    308         // call current drawable's setColorFilter if filter is changed.
    309         reset(mockDrawable);
    310         mDrawableContainer.setColorFilter(null);
    311         verify(mockDrawable, times(1)).setColorFilter(null);
    312 
    313         // does not call it if filter is not changed.
    314         reset(mockDrawable);
    315         mDrawableContainer.setColorFilter(new ColorFilter());
    316         verify(mockDrawable, times(1)).setColorFilter(any());
    317     }
    318 
    319     @Test
    320     public void testSetTint() {
    321         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    322         mDrawableContainer.setTint(Color.BLACK);
    323         mDrawableContainer.setTintMode(Mode.SRC_OVER);
    324 
    325         Drawable dr = spy(new ColorDrawable(Color.GREEN));
    326         addAndSelectDrawable(dr);
    327 
    328         verify(dr, times(1)).setTintMode(Mode.SRC_OVER);
    329 
    330         mDrawableContainer.setTintList(null);
    331         mDrawableContainer.setTintMode(null);
    332         verify(dr, times(1)).setTintMode(null);
    333     }
    334 
    335     @Test
    336     public void testOnBoundsChange() {
    337         mMockDrawableContainer.onBoundsChange(new Rect());
    338         mMockDrawableContainer.onBoundsChange(null);
    339 
    340         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    341         MockDrawable dr = new MockDrawable();
    342         dr.setBounds(new Rect());
    343         addAndSelectDrawable(dr);
    344 
    345         // set current drawable's bounds.
    346         dr.reset();
    347         assertEquals(new Rect(), dr.getBounds());
    348         mMockDrawableContainer.onBoundsChange(new Rect(1, 1, 1, 1));
    349         assertTrue(dr.hasOnBoundsChangedCalled());
    350         assertEquals(new Rect(1, 1, 1, 1), dr.getBounds());
    351 
    352         dr.reset();
    353         mMockDrawableContainer.onBoundsChange(new Rect(1, 1, 1, 1));
    354         assertFalse(dr.hasOnBoundsChangedCalled());
    355         assertEquals(new Rect(1, 1, 1, 1), dr.getBounds());
    356 
    357         try {
    358             mMockDrawableContainer.onBoundsChange(null);
    359             fail("Should throw NullPointerException if the bounds is null.");
    360         } catch (NullPointerException e) {
    361         }
    362     }
    363 
    364     @Test(expected=NullPointerException.class)
    365     public void testIsStatefulNoConstantState() {
    366         // Should throw NullPointerException if the constant state is not set
    367         mDrawableContainer.isStateful();
    368     }
    369 
    370     @Test
    371     public void testIsStateful() {
    372         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    373         Drawable dr0 = spy(new ColorDrawable(Color.YELLOW));
    374         doReturn(true).when(dr0).isStateful();
    375         mDrawableContainerState.addChild(dr0);
    376         Drawable dr1 = spy(new ColorDrawable(Color.GREEN));
    377         doReturn(false).when(dr1).isStateful();
    378         mDrawableContainerState.addChild(dr1);
    379 
    380         // return result of constant state's isStateful
    381         assertEquals(mDrawableContainerState.isStateful(), mDrawableContainer.isStateful());
    382         assertEquals(true, mDrawableContainer.isStateful());
    383 
    384         mDrawableContainer.selectDrawable(1);
    385         assertEquals(mDrawableContainerState.isStateful(), mDrawableContainer.isStateful());
    386         assertEquals(true, mDrawableContainer.isStateful());
    387     }
    388 
    389     @Test
    390     public void testOnStateChange() {
    391         assertFalse(mMockDrawableContainer.onStateChange(new int[] { 0 }));
    392         assertFalse(mMockDrawableContainer.onStateChange(null));
    393 
    394         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    395         MockDrawable dr = new MockDrawable();
    396         dr.setState(new int[] { 0 });
    397         addAndSelectDrawable(dr);
    398 
    399         // set current drawable's state.
    400         dr.reset();
    401         assertNotNull(dr.getState());
    402         mMockDrawableContainer.onStateChange(null);
    403         assertTrue(dr.hasOnStateChangedCalled());
    404         assertNull(dr.getState());
    405 
    406         dr.reset();
    407         mMockDrawableContainer.onStateChange(new int[] { 0 });
    408         assertTrue(dr.hasOnStateChangedCalled());
    409         assertTrue(Arrays.equals(new int[] { 0 }, dr.getState()));
    410 
    411         dr.reset();
    412         assertFalse(mMockDrawableContainer.onStateChange(new int[] { 0 }));
    413         assertFalse(dr.hasOnStateChangedCalled());
    414         assertTrue(Arrays.equals(new int[] { 0 }, dr.getState()));
    415     }
    416 
    417     @Test
    418     public void testOnLevelChange() {
    419         assertFalse(mMockDrawableContainer.onLevelChange(Integer.MAX_VALUE));
    420         assertFalse(mMockDrawableContainer.onLevelChange(Integer.MIN_VALUE));
    421 
    422         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    423         MockDrawable dr = new MockDrawable();
    424         dr.setLevel(0);
    425         addAndSelectDrawable(dr);
    426 
    427         // set current drawable's level.
    428         dr.reset();
    429         assertEquals(0, dr.getLevel());
    430         mMockDrawableContainer.onLevelChange(Integer.MAX_VALUE);
    431         assertEquals(Integer.MAX_VALUE, dr.getLevel());
    432         assertTrue(dr.hasOnLevelChangedCalled());
    433 
    434         dr.reset();
    435         assertEquals(Integer.MAX_VALUE, dr.getLevel());
    436         mMockDrawableContainer.onLevelChange(Integer.MIN_VALUE);
    437         assertEquals(Integer.MIN_VALUE, dr.getLevel());
    438         assertTrue(dr.hasOnLevelChangedCalled());
    439 
    440         dr.reset();
    441         assertEquals(Integer.MIN_VALUE, dr.getLevel());
    442         assertFalse(mMockDrawableContainer.onLevelChange(Integer.MIN_VALUE));
    443         assertEquals(Integer.MIN_VALUE, dr.getLevel());
    444         assertFalse(dr.hasOnLevelChangedCalled());
    445     }
    446 
    447     @Test(expected=NullPointerException.class)
    448     public void testGetIntrinsicWidthNoConstantState() {
    449         // Should throw NullPointerException if the constant state is not set
    450         mDrawableContainer.getIntrinsicWidth();
    451     }
    452 
    453     @Test
    454     public void testGetIntrinsicWidth() {
    455         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    456         Drawable dr0 = spy(new ColorDrawable(Color.RED));
    457         doReturn(1).when(dr0).getIntrinsicWidth();
    458         mDrawableContainerState.addChild(dr0);
    459         Drawable dr1 = spy(new ColorDrawable(Color.GREEN));
    460         doReturn(2).when(dr1).getIntrinsicWidth();
    461         mDrawableContainerState.addChild(dr1);
    462 
    463         // return result of constant state's getConstantWidth
    464         mDrawableContainerState.setConstantSize(true);
    465         assertEquals(mDrawableContainerState.getConstantWidth(),
    466                 mDrawableContainer.getIntrinsicWidth());
    467         assertEquals(2, mDrawableContainer.getIntrinsicWidth());
    468 
    469         // return default value
    470         mDrawableContainerState.setConstantSize(false);
    471         assertNull(mDrawableContainer.getCurrent());
    472         assertEquals(-1, mDrawableContainer.getIntrinsicWidth());
    473 
    474         // return current drawable's getIntrinsicWidth
    475         mDrawableContainer.selectDrawable(0);
    476         assertSame(dr0, mDrawableContainer.getCurrent());
    477         assertEquals(1, mDrawableContainer.getIntrinsicWidth());
    478     }
    479 
    480     @Test(expected=NullPointerException.class)
    481     public void testGetIntrinsicHeightNoConstantState() {
    482         // Should throw NullPointerException if the constant state is not set
    483         mDrawableContainer.getIntrinsicHeight();
    484     }
    485 
    486     @Test
    487     public void testGetIntrinsicHeight() {
    488         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    489         Drawable dr0 = spy(new ColorDrawable(Color.RED));
    490         doReturn(1).when(dr0).getIntrinsicHeight();
    491         mDrawableContainerState.addChild(dr0);
    492         Drawable dr1 = spy(new ColorDrawable(Color.GREEN));
    493         doReturn(2).when(dr1).getIntrinsicHeight();
    494         mDrawableContainerState.addChild(dr1);
    495 
    496         // return result of constant state's getConstantHeight
    497         mDrawableContainerState.setConstantSize(true);
    498         assertEquals(mDrawableContainerState.getConstantHeight(),
    499                 mDrawableContainer.getIntrinsicHeight());
    500         assertEquals(2, mDrawableContainer.getIntrinsicHeight());
    501 
    502         // return default value
    503         mDrawableContainerState.setConstantSize(false);
    504         assertNull(mDrawableContainer.getCurrent());
    505         assertEquals(-1, mDrawableContainer.getIntrinsicHeight());
    506 
    507         // return current drawable's getIntrinsicHeight
    508         mDrawableContainer.selectDrawable(0);
    509         assertSame(dr0, mDrawableContainer.getCurrent());
    510         assertEquals(1, mDrawableContainer.getIntrinsicHeight());
    511     }
    512 
    513     @Test(expected=NullPointerException.class)
    514     public void testGetMinimumWidthNoConstantState() {
    515         // Should throw NullPointerException if the constant state is not set
    516         mDrawableContainer.getMinimumWidth();
    517     }
    518 
    519     @Test
    520     public void testGetMinimumWidth() {
    521         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    522         Drawable dr0 = spy(new ColorDrawable(Color.RED));
    523         doReturn(1).when(dr0).getMinimumWidth();
    524         mDrawableContainerState.addChild(dr0);
    525         Drawable dr1 = spy(new ColorDrawable(Color.RED));
    526         doReturn(2).when(dr1).getMinimumWidth();
    527         mDrawableContainerState.addChild(dr1);
    528 
    529         // return result of constant state's getConstantMinimumWidth
    530         mDrawableContainerState.setConstantSize(true);
    531         assertEquals(mDrawableContainerState.getConstantMinimumWidth(),
    532                 mDrawableContainer.getMinimumWidth());
    533         assertEquals(2, mDrawableContainer.getMinimumWidth());
    534 
    535         // return default value
    536         mDrawableContainerState.setConstantSize(false);
    537         assertNull(mDrawableContainer.getCurrent());
    538         assertEquals(0, mDrawableContainer.getMinimumWidth());
    539 
    540         // return current drawable's getMinimumWidth
    541         mDrawableContainer.selectDrawable(0);
    542         assertSame(dr0, mDrawableContainer.getCurrent());
    543         assertEquals(1, mDrawableContainer.getMinimumWidth());
    544     }
    545 
    546     @Test(expected=NullPointerException.class)
    547     public void testGetMinimumHeightNoConstantState() {
    548         // Should throw NullPointerException if the constant state is not set
    549         mDrawableContainer.getMinimumHeight();
    550     }
    551 
    552     @Test
    553     public void testGetMinimumHeight() {
    554         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    555         Drawable dr0 = spy(new ColorDrawable(Color.RED));
    556         doReturn(1).when(dr0).getMinimumHeight();
    557         mDrawableContainerState.addChild(dr0);
    558         Drawable dr1 = spy(new ColorDrawable(Color.GREEN));
    559         doReturn(2).when(dr1).getMinimumHeight();
    560         mDrawableContainerState.addChild(dr1);
    561 
    562         // return result of constant state's getConstantMinimumHeight
    563         mDrawableContainerState.setConstantSize(true);
    564         assertEquals(mDrawableContainerState.getConstantMinimumHeight(),
    565                 mDrawableContainer.getMinimumHeight());
    566         assertEquals(2, mDrawableContainer.getMinimumHeight());
    567 
    568         // return default value
    569         mDrawableContainerState.setConstantSize(false);
    570         assertNull(mDrawableContainer.getCurrent());
    571         assertEquals(0, mDrawableContainer.getMinimumHeight());
    572 
    573         // return current drawable's getMinimumHeight
    574         mDrawableContainer.selectDrawable(0);
    575         assertSame(dr0, mDrawableContainer.getCurrent());
    576         assertEquals(1, mDrawableContainer.getMinimumHeight());
    577     }
    578 
    579     @Test
    580     public void testInvalidateDrawable() {
    581         mDrawableContainer.setCallback(null);
    582         mDrawableContainer.invalidateDrawable(mDrawableContainer);
    583         mDrawableContainer.invalidateDrawable(null);
    584 
    585         Drawable.Callback callback = mock(Drawable.Callback.class);
    586         mDrawableContainer.setCallback(callback);
    587 
    588         mDrawableContainer.invalidateDrawable(mDrawableContainer);
    589         verify(callback, never()).invalidateDrawable(any());
    590 
    591         // the callback method can be called if the drawable passed in and the
    592         // current drawable are both null
    593         mDrawableContainer.invalidateDrawable(null);
    594         verify(callback, times(1)).invalidateDrawable(any());
    595 
    596         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    597         MockDrawable mockDrawable = new MockDrawable();
    598         addAndSelectDrawable(mockDrawable);
    599 
    600         reset(callback);
    601         mDrawableContainer.invalidateDrawable(mDrawableContainer);
    602         verify(callback, never()).invalidateDrawable(any());
    603 
    604         mDrawableContainer.invalidateDrawable(null);
    605         verify(callback, never()).invalidateDrawable(any());
    606 
    607         // Call the callback method if the drawable is selected.
    608         mDrawableContainer.invalidateDrawable(mockDrawable);
    609         verify(callback, times(1)).invalidateDrawable(any());
    610     }
    611 
    612     @Test
    613     public void testScheduleDrawable() {
    614         mDrawableContainer.setCallback(null);
    615         mDrawableContainer.scheduleDrawable(mDrawableContainer, null, 0);
    616         mDrawableContainer.scheduleDrawable(null, () -> {}, 0);
    617 
    618         Drawable.Callback callback = mock(Drawable.Callback.class);
    619         mDrawableContainer.setCallback(callback);
    620 
    621         mDrawableContainer.scheduleDrawable(mDrawableContainer, null, 0);
    622         verify(callback, never()).scheduleDrawable(any(), any(), anyLong());
    623 
    624         // the callback method can be called if the drawable passed in and the
    625         // current drawble are both null
    626         mDrawableContainer.scheduleDrawable(null, () -> {}, 0);
    627         verify(callback, times(1)).scheduleDrawable(any(), any(), anyLong());
    628 
    629         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    630         MockDrawable mockDrawable = new MockDrawable();
    631         addAndSelectDrawable(mockDrawable);
    632 
    633         reset(callback);
    634         mDrawableContainer.scheduleDrawable(mDrawableContainer, null, 0);
    635         verify(callback, never()).scheduleDrawable(any(), any(), anyLong());
    636         verify(callback, never()).scheduleDrawable(any(), any(), anyLong());
    637 
    638         mDrawableContainer.scheduleDrawable(null, () -> {}, 0);
    639         verify(callback, never()).scheduleDrawable(any(), any(), anyLong());
    640 
    641         // Call the callback method if the drawable is selected.
    642         mDrawableContainer.scheduleDrawable(mockDrawable, null, 0);
    643         verify(callback, times(1)).scheduleDrawable(any(), any(), anyLong());
    644     }
    645 
    646     @Test
    647     public void testUnscheduleDrawable() {
    648         mDrawableContainer.setCallback(null);
    649         mDrawableContainer.unscheduleDrawable(mDrawableContainer, null);
    650         mDrawableContainer.unscheduleDrawable(null, () -> {});
    651 
    652         Drawable.Callback callback = mock(Drawable.Callback.class);
    653         mDrawableContainer.setCallback(callback);
    654 
    655         mDrawableContainer.unscheduleDrawable(mDrawableContainer, null);
    656         verify(callback, never()).unscheduleDrawable(any(), any());
    657 
    658         // the callback method can be called if the drawable passed in and the
    659         // current drawble are both null
    660         mDrawableContainer.unscheduleDrawable(null, () -> {});
    661         verify(callback, times(1)).unscheduleDrawable(any(), any());
    662 
    663         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    664         MockDrawable mockDrawable = new MockDrawable();
    665         addAndSelectDrawable(mockDrawable);
    666 
    667         reset(callback);
    668         mDrawableContainer.unscheduleDrawable(mDrawableContainer, null);
    669         verify(callback, never()).unscheduleDrawable(any(), any());
    670 
    671         mDrawableContainer.unscheduleDrawable(null, () -> {});
    672         verify(callback, never()).unscheduleDrawable(any(), any());
    673 
    674         // Call the callback method if the drawable is selected.
    675         mDrawableContainer.unscheduleDrawable(mockDrawable, null);
    676         verify(callback, times(1)).unscheduleDrawable(any(), any());
    677     }
    678 
    679     @Test
    680     public void testSetVisible() {
    681         assertTrue(mDrawableContainer.isVisible());
    682         assertFalse(mDrawableContainer.setVisible(true, false));
    683         assertTrue(mDrawableContainer.setVisible(false, false));
    684         assertFalse(mDrawableContainer.setVisible(false, false));
    685         assertTrue(mDrawableContainer.setVisible(true, false));
    686 
    687         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    688         MockDrawable dr = new MockDrawable();
    689         addAndSelectDrawable(dr);
    690 
    691         // set current drawable's visibility
    692         assertTrue(mDrawableContainer.isVisible());
    693         assertTrue(dr.isVisible());
    694         assertTrue(mDrawableContainer.setVisible(false, false));
    695         assertFalse(mDrawableContainer.isVisible());
    696         assertFalse(dr.isVisible());
    697     }
    698 
    699     @Test
    700     public void testGetOpacity() {
    701         // there is no child, so the container is transparent
    702         assertEquals(PixelFormat.TRANSPARENT, mDrawableContainer.getOpacity());
    703 
    704         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    705         Drawable dr0 = spy(new ColorDrawable(Color.GREEN));
    706         doReturn(PixelFormat.OPAQUE).when(dr0).getOpacity();
    707         mDrawableContainerState.addChild(dr0);
    708         // no child selected yet
    709         assertEquals(PixelFormat.TRANSPARENT, mDrawableContainer.getOpacity());
    710 
    711         mDrawableContainer.selectDrawable(0);
    712         assertEquals(mDrawableContainerState.getOpacity(), mDrawableContainer.getOpacity());
    713         assertEquals(PixelFormat.OPAQUE, mDrawableContainer.getOpacity());
    714 
    715         Drawable dr1 = spy(new ColorDrawable(Color.RED));
    716         doReturn(PixelFormat.TRANSLUCENT).when(dr1).getOpacity();
    717         mDrawableContainerState.addChild(dr1);
    718 
    719         mDrawableContainer.selectDrawable(1);
    720         assertEquals(mDrawableContainerState.getOpacity(), mDrawableContainer.getOpacity());
    721         assertEquals(PixelFormat.TRANSLUCENT, mDrawableContainer.getOpacity());
    722     }
    723 
    724     @Test(expected=NullPointerException.class)
    725     public void testSelectDrawableNoConstantState() {
    726         // Should throw NullPointerException if the constant state is not set
    727         mDrawableContainer.selectDrawable(0);
    728     }
    729 
    730     @Test
    731     public void testSelectDrawable() {
    732         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    733         MockDrawable dr0 = new MockDrawable();
    734         dr0.setVisible(false, false);
    735         assertFalse(dr0.isVisible());
    736         mDrawableContainerState.addChild(dr0);
    737         MockDrawable dr1 = new MockDrawable();
    738         dr1.setVisible(false, false);
    739         assertFalse(dr1.isVisible());
    740         mDrawableContainerState.addChild(dr1);
    741 
    742         assertTrue(mDrawableContainer.selectDrawable(0));
    743         assertSame(dr0, mDrawableContainer.getCurrent());
    744         assertTrue(dr0.isVisible());
    745 
    746         assertFalse(mDrawableContainer.selectDrawable(0));
    747 
    748         assertTrue(mDrawableContainer.selectDrawable(1));
    749         assertSame(dr1, mDrawableContainer.getCurrent());
    750         assertTrue(dr1.isVisible());
    751         assertFalse(dr0.isVisible());
    752 
    753         assertFalse(mDrawableContainer.selectDrawable(1));
    754 
    755         assertTrue(mDrawableContainer.selectDrawable(-1));
    756         assertNull(mDrawableContainer.getCurrent());
    757         assertFalse(dr0.isVisible());
    758         assertFalse(dr1.isVisible());
    759 
    760         assertTrue(mDrawableContainer.selectDrawable(2));
    761         assertNull(mDrawableContainer.getCurrent());
    762         assertFalse(dr0.isVisible());
    763         assertFalse(dr1.isVisible());
    764     }
    765 
    766     @Test
    767     public void testAccessConstantState() {
    768         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    769         assertSame(mDrawableContainerState, mDrawableContainer.getConstantState());
    770 
    771         mMockDrawableContainer.setConstantState(null);
    772         // Note that we're not using 'expected' on the @Test annotation since we want to
    773         // make sure that only this next call is going to throw NPE.
    774         try {
    775             mDrawableContainer.getConstantState();
    776             fail("Should throw NullPointerException.");
    777         } catch (NullPointerException e) {
    778         }
    779     }
    780 
    781     @Test(expected=NullPointerException.class)
    782     public void testMutateNoConstantState() {
    783         // Should throw NullPointerException if the constant state is not set
    784         mDrawableContainer.mutate();
    785     }
    786 
    787     @Test
    788     public void testMutate() {
    789         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    790         Drawable dr0 = spy(new ColorDrawable(Color.MAGENTA));
    791         mDrawableContainerState.addChild(dr0);
    792         mDrawableContainer.mutate();
    793         verify(dr0, atLeastOnce()).mutate();
    794     }
    795 
    796     @Test
    797     public void testOpacityChange() {
    798         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    799         ColorDrawable c1 = new ColorDrawable(Color.RED);
    800         ColorDrawable c2 = new ColorDrawable(Color.BLUE);
    801         addAndSelectDrawable(c1);
    802         addAndSelectDrawable(c2);
    803         assertEquals(PixelFormat.OPAQUE, mDrawableContainer.getOpacity());
    804 
    805         // Changes to the not-current drawable should still refresh.
    806         c1.setTint(0x80FF0000);
    807         c1.setTintMode(PorterDuff.Mode.SRC);
    808         assertEquals(PixelFormat.TRANSLUCENT, mDrawableContainer.getOpacity());
    809     }
    810 
    811     @Test
    812     public void testStatefulnessChange() {
    813         mMockDrawableContainer.setConstantState(mDrawableContainerState);
    814         ColorDrawable c1 = new ColorDrawable(Color.RED);
    815         ColorDrawable c2 = new ColorDrawable(Color.BLUE);
    816         addAndSelectDrawable(c1);
    817         addAndSelectDrawable(c2);
    818         assertEquals(false, mDrawableContainer.isStateful());
    819 
    820         // Changes to the not-current drawable should still refresh.
    821         ColorStateList csl = new ColorStateList(
    822                 new int[][] { { android.R.attr.state_enabled }, { } },
    823                 new int[] { Color.RED, Color.BLUE });
    824         c1.setTintList(csl);
    825         assertEquals(true, mDrawableContainer.isStateful());
    826     }
    827 
    828     private void addAndSelectDrawable(Drawable drawable) {
    829         int pos = mDrawableContainerState.addChild(drawable);
    830         mDrawableContainer.selectDrawable(pos);
    831         assertSame(drawable, mDrawableContainer.getCurrent());
    832     }
    833 
    834     private class MockDrawableContainer extends DrawableContainer {
    835         @Override
    836         protected void onBoundsChange(Rect bounds) {
    837             super.onBoundsChange(bounds);
    838         }
    839 
    840         @Override
    841         protected boolean onLevelChange(int level) {
    842             return super.onLevelChange(level);
    843         }
    844 
    845         @Override
    846         protected boolean onStateChange(int[] state) {
    847             return super.onStateChange(state);
    848         }
    849 
    850         @Override
    851         protected void setConstantState(DrawableContainerState state) {
    852             super.setConstantState(state);
    853         }
    854     }
    855 
    856     // Since Mockito can't mock or spy on protected methods, we have a custom extension
    857     // of Drawable to track calls to protected methods. This class also has empty implementations
    858     // of the base abstract methods.
    859     private class MockDrawable extends Drawable {
    860         private boolean mHasCalledOnBoundsChanged;
    861         private boolean mHasCalledOnStateChanged;
    862         private boolean mHasCalledOnLevelChanged;
    863 
    864         @Override
    865         public int getOpacity() {
    866             return PixelFormat.OPAQUE;
    867         }
    868 
    869         @Override
    870         public void draw(Canvas canvas) {
    871         }
    872 
    873         @Override
    874         public void setAlpha(int alpha) {
    875         }
    876 
    877         @Override
    878         public void setColorFilter(ColorFilter colorFilter) {
    879         }
    880 
    881         public boolean hasOnBoundsChangedCalled() {
    882             return mHasCalledOnBoundsChanged;
    883         }
    884 
    885         public boolean hasOnStateChangedCalled() {
    886             return mHasCalledOnStateChanged;
    887         }
    888 
    889         public boolean hasOnLevelChangedCalled() {
    890             return mHasCalledOnLevelChanged;
    891         }
    892 
    893         public void reset() {
    894             mHasCalledOnLevelChanged = false;
    895             mHasCalledOnStateChanged = false;
    896             mHasCalledOnBoundsChanged = false;
    897         }
    898 
    899         @Override
    900         protected void onBoundsChange(Rect bounds) {
    901             super.onBoundsChange(bounds);
    902             mHasCalledOnBoundsChanged = true;
    903         }
    904 
    905         @Override
    906         protected boolean onLevelChange(int level) {
    907             boolean result = super.onLevelChange(level);
    908             mHasCalledOnLevelChanged = true;
    909             return result;
    910         }
    911 
    912         @Override
    913         protected boolean onStateChange(int[] state) {
    914             boolean result = super.onStateChange(state);
    915             mHasCalledOnStateChanged = true;
    916             return result;
    917         }
    918     }
    919 }
    920