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.eq;
     27 import static org.mockito.Mockito.mock;
     28 import static org.mockito.Mockito.times;
     29 import static org.mockito.Mockito.verify;
     30 
     31 import android.content.ContentResolver;
     32 import android.content.Context;
     33 import android.content.res.Resources;
     34 import android.content.res.Resources.Theme;
     35 import android.graphics.BitmapFactory;
     36 import android.graphics.BlendMode;
     37 import android.graphics.Canvas;
     38 import android.graphics.ColorFilter;
     39 import android.graphics.Insets;
     40 import android.graphics.PixelFormat;
     41 import android.graphics.PorterDuff;
     42 import android.graphics.Rect;
     43 import android.graphics.cts.R;
     44 import android.graphics.drawable.Drawable;
     45 import android.graphics.drawable.Drawable.Callback;
     46 import android.net.Uri;
     47 import android.util.AttributeSet;
     48 import android.util.StateSet;
     49 import android.util.TypedValue;
     50 import android.util.Xml;
     51 import android.view.View;
     52 
     53 import androidx.test.InstrumentationRegistry;
     54 import androidx.test.filters.SmallTest;
     55 import androidx.test.runner.AndroidJUnit4;
     56 
     57 import org.junit.Before;
     58 import org.junit.Test;
     59 import org.junit.runner.RunWith;
     60 import org.xmlpull.v1.XmlPullParser;
     61 import org.xmlpull.v1.XmlPullParserException;
     62 
     63 import java.io.ByteArrayOutputStream;
     64 import java.io.File;
     65 import java.io.FileInputStream;
     66 import java.io.FileOutputStream;
     67 import java.io.IOException;
     68 import java.io.InputStream;
     69 import java.io.OutputStream;
     70 
     71 @SmallTest
     72 @RunWith(AndroidJUnit4.class)
     73 public class DrawableTest {
     74     private Context mContext;
     75     private Resources mResources;
     76 
     77     @Before
     78     public void setup() {
     79         mContext = InstrumentationRegistry.getTargetContext();
     80         mResources = mContext.getResources();
     81     }
     82 
     83     @Test
     84     public void testClearColorFilter() {
     85         Drawable mockDrawable = new MockDrawable();
     86         mockDrawable.clearColorFilter();
     87         assertNull(mockDrawable.getColorFilter());
     88 
     89         ColorFilter cf = new ColorFilter();
     90         mockDrawable.setColorFilter(cf);
     91         assertEquals(cf, mockDrawable.getColorFilter());
     92 
     93         mockDrawable.clearColorFilter();
     94         assertNull(mockDrawable.getColorFilter());
     95     }
     96 
     97     @Test
     98     public void testCopyBounds() {
     99         Drawable mockDrawable = new MockDrawable();
    100         Rect rect1 = mockDrawable.copyBounds();
    101         Rect r1 = new Rect();
    102         mockDrawable.copyBounds(r1);
    103         assertEquals(0, rect1.bottom);
    104         assertEquals(0, rect1.left);
    105         assertEquals(0, rect1.right);
    106         assertEquals(0, rect1.top);
    107         assertEquals(0, r1.bottom);
    108         assertEquals(0, r1.left);
    109         assertEquals(0, r1.right);
    110         assertEquals(0, r1.top);
    111 
    112         mockDrawable.setBounds(10, 10, 100, 100);
    113         Rect rect2 = mockDrawable.copyBounds();
    114         Rect r2 = new Rect();
    115         mockDrawable.copyBounds(r2);
    116         assertEquals(100, rect2.bottom);
    117         assertEquals(10, rect2.left);
    118         assertEquals(100, rect2.right);
    119         assertEquals(10, rect2.top);
    120         assertEquals(100, r2.bottom);
    121         assertEquals(10, r2.left);
    122         assertEquals(100, r2.right);
    123         assertEquals(10, r2.top);
    124 
    125         mockDrawable.setBounds(new Rect(50, 50, 500, 500));
    126         Rect rect3 = mockDrawable.copyBounds();
    127         Rect r3 = new Rect();
    128         mockDrawable.copyBounds(r3);
    129         assertEquals(500, rect3.bottom);
    130         assertEquals(50, rect3.left);
    131         assertEquals(500, rect3.right);
    132         assertEquals(50, rect3.top);
    133         assertEquals(500, r3.bottom);
    134         assertEquals(50, r3.left);
    135         assertEquals(500, r3.right);
    136         assertEquals(50, r3.top);
    137 
    138         try {
    139             mockDrawable.copyBounds(null);
    140             fail("should throw NullPointerException.");
    141         } catch (NullPointerException e) {
    142         }
    143     }
    144 
    145     @Test
    146     public void testCreateFromPath() throws IOException {
    147         assertNull(Drawable.createFromPath(null));
    148 
    149         Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
    150                 mContext.getPackageName() + R.raw.testimage);
    151         assertNull(Drawable.createFromPath(uri.getPath()));
    152 
    153         File imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
    154         assertTrue(imageFile.createNewFile());
    155         assertTrue(imageFile.exists());
    156         writeSampleImage(imageFile);
    157 
    158         final String path = imageFile.getPath();
    159         Uri u = Uri.parse(path);
    160         assertNotNull(Drawable.createFromPath(u.toString()));
    161         assertTrue(imageFile.delete());
    162     }
    163 
    164     @Test
    165     public void testCreateFromIncomplete() throws IOException {
    166         // Create a truncated image file.
    167         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    168         byte[] buffer = new byte[1024];
    169         try (InputStream source = mResources.openRawResource(R.raw.testimage)) {
    170             for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) {
    171                 bytes.write(buffer, 0, len);
    172             }
    173         }
    174 
    175         File imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
    176         assertTrue(imageFile.createNewFile());
    177         assertTrue(imageFile.exists());
    178         try (OutputStream target = new FileOutputStream(imageFile)) {
    179             byte[] byteArray = bytes.toByteArray();
    180             target.write(byteArray, 0, byteArray.length / 2);
    181         }
    182 
    183         // Now test various Drawable APIs that should succeed.
    184         final String path = imageFile.getPath();
    185         Uri u = Uri.parse(path);
    186         assertNotNull(Drawable.createFromPath(u.toString()));
    187 
    188         try (FileInputStream input = new FileInputStream(imageFile)) {
    189             assertNotNull(Drawable.createFromStream(input, ""));
    190         }
    191 
    192         try (FileInputStream input = new FileInputStream(imageFile)) {
    193             assertNotNull(Drawable.createFromResourceStream(mResources, new TypedValue(),
    194                         input, ""));
    195         }
    196 
    197         try (FileInputStream input = new FileInputStream(imageFile)) {
    198             assertNotNull(Drawable.createFromResourceStream(mResources, new TypedValue(),
    199                         input, "", new BitmapFactory.Options()));
    200         }
    201 
    202         assertTrue(imageFile.delete());
    203     }
    204 
    205     private void writeSampleImage(File imagefile) throws IOException {
    206         try (InputStream source = mResources.openRawResource(R.raw.testimage);
    207              OutputStream target = new FileOutputStream(imagefile)) {
    208             byte[] buffer = new byte[1024];
    209             for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) {
    210                 target.write(buffer, 0, len);
    211             }
    212         }
    213     }
    214 
    215     @Test
    216     public void testCreateFromStream() throws IOException {
    217         FileInputStream inputEmptyStream = null;
    218         FileInputStream inputStream = null;
    219         File imageFile = null;
    220         OutputStream outputEmptyStream = null;
    221 
    222         assertNull(Drawable.createFromStream(null, "test.bmp"));
    223 
    224         File emptyFile = new File(mContext.getFilesDir(), "tempemptyimage.jpg");
    225 
    226         // write some random data.
    227         try {
    228             outputEmptyStream = new FileOutputStream(emptyFile);
    229             outputEmptyStream.write(10);
    230 
    231             inputEmptyStream = new FileInputStream(emptyFile);
    232             assertNull(Drawable.createFromStream(inputEmptyStream, "Sample"));
    233 
    234             imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
    235 
    236             writeSampleImage(imageFile);
    237 
    238             inputStream = new FileInputStream(imageFile);
    239             assertNotNull(Drawable.createFromStream(inputStream, "Sample"));
    240         } finally {
    241             if (null != outputEmptyStream) {
    242                 outputEmptyStream.close();
    243             }
    244             if (null != inputEmptyStream) {
    245                 inputEmptyStream.close();
    246             }
    247             if (null != inputStream) {
    248                 inputStream.close();
    249             }
    250             if (emptyFile.exists()) {
    251                 assertTrue(emptyFile.delete());
    252             }
    253             if (imageFile.exists()) {
    254                 assertTrue(imageFile.delete());
    255             }
    256         }
    257     }
    258 
    259     private Boolean isClosed = new Boolean(false);
    260 
    261     @Test
    262     public void testCreateFromStream2() throws IOException {
    263         FileInputStream inputStream = null;
    264         File imageFile = null;
    265         synchronized (isClosed) {
    266             isClosed = false;
    267             try {
    268                 imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
    269                 writeSampleImage(imageFile);
    270 
    271                 inputStream = new FileInputStream(imageFile) {
    272                     @Override
    273                     public void close() throws IOException {
    274                         super.close();
    275                         isClosed = true;
    276                     }
    277                 };
    278                 assertNotNull(Drawable.createFromStream(inputStream, "Sample"));
    279             } finally {
    280                 if (null != inputStream) {
    281 		    // verify that the stream was not closed
    282                     assertFalse(isClosed);
    283                     inputStream.close();
    284                 }
    285                 if (imageFile.exists()) {
    286                     assertTrue(imageFile.delete());
    287                 }
    288             }
    289         }
    290     }
    291 
    292     @Test
    293     public void testCreateFromResourceStream1() throws IOException {
    294         FileInputStream inputEmptyStream = null;
    295         FileInputStream inputStream = null;
    296         File imageFile = null;
    297         OutputStream outputEmptyStream = null;
    298 
    299         assertNull(Drawable.createFromResourceStream(null, null, null, "test.bmp"));
    300 
    301         File emptyFile = new File(mContext.getFilesDir(), "tempemptyimage.jpg");
    302 
    303         // write some random data.
    304         try {
    305             outputEmptyStream = new FileOutputStream(emptyFile);
    306             outputEmptyStream.write(10);
    307 
    308             inputEmptyStream = new FileInputStream(emptyFile);
    309             assertNull(Drawable.createFromResourceStream(mResources, null, inputEmptyStream,
    310                     "Sample"));
    311 
    312             imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
    313 
    314             writeSampleImage(imageFile);
    315 
    316             inputStream = new FileInputStream(imageFile);
    317             final TypedValue value = new TypedValue();
    318             assertNotNull(Drawable.createFromResourceStream(mResources, value, inputStream,
    319                     "Sample"));
    320         } finally {
    321             if (null != outputEmptyStream) {
    322                 outputEmptyStream.close();
    323             }
    324             if (null != inputEmptyStream) {
    325                 inputEmptyStream.close();
    326             }
    327             if (null != inputStream) {
    328                 inputStream.close();
    329             }
    330             if (emptyFile.exists()) {
    331                 assertTrue(emptyFile.delete());
    332             }
    333             if (imageFile.exists()) {
    334                 assertTrue(imageFile.delete());
    335             }
    336         }
    337     }
    338 
    339     @Test
    340     public void testCreateFromResourceStream2() throws IOException {
    341         FileInputStream inputEmptyStream = null;
    342         FileInputStream inputStream = null;
    343         File imageFile = null;
    344         OutputStream outputEmptyStream = null;
    345 
    346         BitmapFactory.Options opt = new BitmapFactory.Options();
    347         opt.inScaled = false;
    348 
    349         assertNull(Drawable.createFromResourceStream(null, null, null, "test.bmp", opt));
    350 
    351         File emptyFile = new File(mContext.getFilesDir(), "tempemptyimage.jpg");
    352 
    353         // write some random data.
    354         try {
    355             outputEmptyStream = new FileOutputStream(emptyFile);
    356             outputEmptyStream.write(10);
    357 
    358             inputEmptyStream = new FileInputStream(emptyFile);
    359             assertNull(Drawable.createFromResourceStream(mResources, null, inputEmptyStream,
    360                     "Sample", opt));
    361 
    362             imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
    363 
    364             writeSampleImage(imageFile);
    365 
    366             inputStream = new FileInputStream(imageFile);
    367             final TypedValue value = new TypedValue();
    368             assertNotNull(Drawable.createFromResourceStream(mResources, value, inputStream,
    369                     "Sample", opt));
    370         } finally {
    371             if (null != outputEmptyStream) {
    372                 outputEmptyStream.close();
    373             }
    374             if (null != inputEmptyStream) {
    375                 inputEmptyStream.close();
    376             }
    377             if (null != inputStream) {
    378                 inputStream.close();
    379             }
    380             if (emptyFile.exists()) {
    381                 assertTrue(emptyFile.delete());
    382             }
    383             if (imageFile.exists()) {
    384                 assertTrue(imageFile.delete());
    385             }
    386         }
    387     }
    388 
    389     @Test
    390     public void testImageIntrinsicScaledForDensity() throws IOException {
    391         try (InputStream is = mContext.getAssets().open("green-p3.png")) {
    392             Drawable drawable = Drawable.createFromStream(is, null);
    393             assertNotNull(drawable);
    394 
    395             float density = mContext.getResources().getDisplayMetrics().density;
    396             int densityAdjustedSize = Math.round(64 / density);
    397             assertEquals(densityAdjustedSize, drawable.getIntrinsicWidth());
    398             assertEquals(densityAdjustedSize, drawable.getIntrinsicHeight());
    399         }
    400     }
    401 
    402     @Test
    403     public void testImageIntrinsicScaledForDensityWithBitmapOptions() throws IOException {
    404         try (InputStream is = mContext.getAssets().open("green-p3.png")) {
    405             // Verify that providing BitmapFactory Options provides the same result
    406             Drawable drawable = Drawable.createFromResourceStream(
    407                     null, null, is, null, new BitmapFactory.Options());
    408             assertNotNull(drawable);
    409 
    410             float density = mContext.getResources().getDisplayMetrics().density;
    411             int densityAdjustedSize = Math.round(64 / density);
    412             assertEquals(densityAdjustedSize, drawable.getIntrinsicWidth());
    413             assertEquals(densityAdjustedSize, drawable.getIntrinsicHeight());
    414         }
    415     }
    416 
    417     @Test
    418     public void testCreateFromXml() throws XmlPullParserException, IOException {
    419         XmlPullParser parser = mResources.getXml(R.drawable.gradientdrawable);
    420         Drawable drawable = Drawable.createFromXml(mResources, parser);
    421         assertNotNull(drawable);
    422 
    423         Drawable expected = mResources.getDrawable(R.drawable.gradientdrawable, null);
    424         assertEquals(expected.getIntrinsicWidth(), drawable.getIntrinsicWidth());
    425         assertEquals(expected.getIntrinsicHeight(), drawable.getIntrinsicHeight());
    426     }
    427 
    428     @Test
    429     public void testCreateFromXmlThemed() throws XmlPullParserException, IOException {
    430         XmlPullParser parser = mResources.getXml(R.drawable.gradientdrawable_theme);
    431         Theme theme = mResources.newTheme();
    432         theme.applyStyle(R.style.Theme_ThemedDrawableTest, true);
    433         Drawable drawable = Drawable.createFromXml(mResources, parser, theme);
    434         assertNotNull(drawable);
    435 
    436         Drawable expected = mResources.getDrawable(R.drawable.gradientdrawable_theme, theme);
    437         assertEquals(expected.getIntrinsicWidth(), drawable.getIntrinsicWidth());
    438         assertEquals(expected.getIntrinsicHeight(), drawable.getIntrinsicHeight());
    439     }
    440 
    441     @Test
    442     public void testCreateFromXmlInner() throws XmlPullParserException, IOException {
    443         XmlPullParser parser = mResources.getXml(R.drawable.gradientdrawable);
    444         while (parser.next() != XmlPullParser.START_TAG) {
    445             // ignore event, just seek to first tag
    446         }
    447         AttributeSet attrs = Xml.asAttributeSet(parser);
    448         Drawable drawable = Drawable.createFromXmlInner(mResources, parser, attrs);
    449         assertNotNull(drawable);
    450 
    451         Drawable expected = mResources.getDrawable(R.drawable.gradientdrawable, null);
    452         assertEquals(expected.getIntrinsicWidth(), drawable.getIntrinsicWidth());
    453         assertEquals(expected.getIntrinsicHeight(), drawable.getIntrinsicHeight());
    454     }
    455 
    456     @Test
    457     public void testCreateFromXmlInnerThemed() throws XmlPullParserException, IOException {
    458         XmlPullParser parser = mResources.getXml(R.drawable.gradientdrawable_theme);
    459         while (parser.next() != XmlPullParser.START_TAG) {
    460             // ignore event, just seek to first tag
    461         }
    462         AttributeSet attrs = Xml.asAttributeSet(parser);
    463         Theme theme = mResources.newTheme();
    464         theme.applyStyle(R.style.Theme_ThemedDrawableTest, true);
    465         Drawable drawable = Drawable.createFromXmlInner(mResources, parser, attrs, theme);
    466         assertNotNull(drawable);
    467 
    468         Drawable expected = mResources.getDrawable(R.drawable.gradientdrawable_theme, theme);
    469         assertEquals(expected.getIntrinsicWidth(), drawable.getIntrinsicWidth());
    470         assertEquals(expected.getIntrinsicHeight(), drawable.getIntrinsicHeight());
    471     }
    472 
    473     @Test
    474     public void testAccessBounds() {
    475         Drawable mockDrawable = new MockDrawable();
    476         mockDrawable.setBounds(0, 0, 100, 100);
    477         Rect r = mockDrawable.getBounds();
    478         assertEquals(0, r.left);
    479         assertEquals(0, r.top);
    480         assertEquals(100, r.bottom);
    481         assertEquals(100, r.right);
    482 
    483         mockDrawable.setBounds(new Rect(10, 10, 150, 150));
    484         r = mockDrawable.getBounds();
    485         assertEquals(10, r.left);
    486         assertEquals(10, r.top);
    487         assertEquals(150, r.bottom);
    488         assertEquals(150, r.right);
    489 
    490         try {
    491             mockDrawable.setBounds(null);
    492             fail("should throw NullPointerException.");
    493         } catch (NullPointerException e) {
    494         }
    495     }
    496 
    497     @Test
    498     public void testAccessChangingConfigurations() {
    499         Drawable mockDrawable = new MockDrawable();
    500         assertEquals(0, mockDrawable.getChangingConfigurations());
    501 
    502         mockDrawable.setChangingConfigurations(1);
    503         assertEquals(1, mockDrawable.getChangingConfigurations());
    504 
    505         mockDrawable.setChangingConfigurations(Integer.MAX_VALUE);
    506         assertEquals(Integer.MAX_VALUE, mockDrawable.getChangingConfigurations());
    507 
    508         mockDrawable.setChangingConfigurations(Integer.MIN_VALUE);
    509         assertEquals(Integer.MIN_VALUE, mockDrawable.getChangingConfigurations());
    510     }
    511 
    512     @Test
    513     public void testGetConstantState() {
    514         Drawable mockDrawable = new MockDrawable();
    515         assertNull(mockDrawable.getConstantState());
    516     }
    517 
    518     @Test
    519     public void testGetCurrent() {
    520         Drawable mockDrawable = new MockDrawable();
    521         assertSame(mockDrawable, mockDrawable.getCurrent());
    522     }
    523 
    524     @Test
    525     public void testGetIntrinsicHeight() {
    526         Drawable mockDrawable = new MockDrawable();
    527         assertEquals(-1, mockDrawable.getIntrinsicHeight());
    528     }
    529 
    530     @Test
    531     public void testGetIntrinsicWidth() {
    532         Drawable mockDrawable = new MockDrawable();
    533         assertEquals(-1, mockDrawable.getIntrinsicWidth());
    534     }
    535 
    536     @Test
    537     public void testAccessLevel() {
    538         Drawable mockDrawable = new MockDrawable();
    539         assertEquals(0, mockDrawable.getLevel());
    540 
    541         assertFalse(mockDrawable.setLevel(10));
    542         assertEquals(10, mockDrawable.getLevel());
    543 
    544         assertFalse(mockDrawable.setLevel(20));
    545         assertEquals(20, mockDrawable.getLevel());
    546 
    547         assertFalse(mockDrawable.setLevel(0));
    548         assertEquals(0, mockDrawable.getLevel());
    549 
    550         assertFalse(mockDrawable.setLevel(10000));
    551         assertEquals(10000, mockDrawable.getLevel());
    552     }
    553 
    554     @Test
    555     public void testGetMinimumHeight() {
    556         Drawable mockDrawable = new MockDrawable();
    557         assertEquals(0, mockDrawable.getMinimumHeight());
    558     }
    559 
    560     @Test
    561     public void testGetMinimumWidth() {
    562         Drawable mockDrawable = new MockDrawable();
    563         assertEquals(0, mockDrawable.getMinimumWidth());
    564     }
    565 
    566     @Test
    567     public void testGetPadding() {
    568         Drawable mockDrawable = new MockDrawable();
    569         Rect r = new Rect(10, 10, 20, 20);
    570         assertFalse(mockDrawable.getPadding(r));
    571         assertEquals(0, r.bottom);
    572         assertEquals(0, r.top);
    573         assertEquals(0, r.left);
    574         assertEquals(0, r.right);
    575 
    576         try {
    577             mockDrawable.getPadding(null);
    578             fail("should throw NullPointerException.");
    579         } catch (NullPointerException e) {
    580         }
    581     }
    582 
    583     @Test
    584     public void testAccessState() {
    585         Drawable mockDrawable = new MockDrawable();
    586         assertEquals(StateSet.WILD_CARD, mockDrawable.getState());
    587 
    588         int[] states = new int[] {1, 2, 3};
    589         assertFalse(mockDrawable.setState(states));
    590         assertEquals(states, mockDrawable.getState());
    591 
    592         mockDrawable.setState(null);
    593     }
    594 
    595     @Test
    596     public void testGetTransparentRegion() {
    597         Drawable mockDrawable = new MockDrawable();
    598         assertNull(mockDrawable.getTransparentRegion());
    599     }
    600 
    601     @Test
    602     public void testInflate() throws XmlPullParserException, IOException {
    603         Drawable mockDrawable = new MockDrawable();
    604 
    605         XmlPullParser parser = mResources.getXml(R.xml.drawable_test);
    606         while (parser.next() != XmlPullParser.START_TAG) {
    607             // ignore event, just seek to first tag
    608         }
    609         AttributeSet attrs = Xml.asAttributeSet(parser);
    610 
    611         mockDrawable.inflate(mResources, parser, attrs);
    612         // visibility set to false in resource
    613         assertFalse(mockDrawable.isVisible());
    614     }
    615 
    616     @Test
    617     public void testInvalidateSelf() {
    618         Drawable mockDrawable = new MockDrawable();
    619         // if setCallback() is not called, invalidateSelf() would do nothing,
    620         // so just call it to check whether it throws exceptions.
    621         mockDrawable.invalidateSelf();
    622 
    623         Drawable.Callback mockCallback = mock(Drawable.Callback.class);
    624         mockDrawable.setCallback(mockCallback);
    625         mockDrawable.invalidateSelf();
    626         verify(mockCallback, times(1)).invalidateDrawable(mockDrawable);
    627     }
    628 
    629     @Test
    630     public void testIsStateful() {
    631         Drawable mockDrawable = new MockDrawable();
    632         assertFalse(mockDrawable.isStateful());
    633     }
    634 
    635     @Test
    636     public void testVisible() {
    637         Drawable mockDrawable = new MockDrawable();
    638         assertTrue(mockDrawable.isVisible());
    639 
    640         assertTrue(mockDrawable.setVisible(false, false));
    641         assertFalse(mockDrawable.isVisible());
    642 
    643         assertFalse(mockDrawable.setVisible(false, false));
    644         assertFalse(mockDrawable.isVisible());
    645 
    646         assertTrue(mockDrawable.setVisible(true, false));
    647         assertTrue(mockDrawable.isVisible());
    648     }
    649 
    650     @Test
    651     public void testOnBoundsChange() {
    652         MockDrawable mockDrawable = new MockDrawable();
    653 
    654         // No-op in the Drawable superclass.
    655         mockDrawable.onBoundsChange(new Rect(0, 0, 10, 10));
    656     }
    657 
    658     @Test
    659     public void testOnLevelChange() {
    660         MockDrawable mockDrawable = new MockDrawable();
    661         assertFalse(mockDrawable.onLevelChange(0));
    662     }
    663 
    664     @Test
    665     public void testOnStateChange() {
    666         MockDrawable mockDrawable = new MockDrawable();
    667         assertFalse(mockDrawable.onStateChange(null));
    668     }
    669 
    670     @Test
    671     public void testResolveOpacity() {
    672         assertEquals(PixelFormat.TRANSLUCENT,
    673                 Drawable.resolveOpacity(PixelFormat.TRANSLUCENT, PixelFormat.TRANSLUCENT));
    674         assertEquals(PixelFormat.UNKNOWN,
    675                 Drawable.resolveOpacity(PixelFormat.UNKNOWN, PixelFormat.TRANSLUCENT));
    676         assertEquals(PixelFormat.TRANSLUCENT,
    677                 Drawable.resolveOpacity(PixelFormat.OPAQUE, PixelFormat.TRANSLUCENT));
    678         assertEquals(PixelFormat.TRANSPARENT,
    679                 Drawable.resolveOpacity(PixelFormat.OPAQUE, PixelFormat.TRANSPARENT));
    680         assertEquals(PixelFormat.OPAQUE,
    681                 Drawable.resolveOpacity(PixelFormat.RGB_888, PixelFormat.RGB_565));
    682     }
    683 
    684     @Test
    685     public void testScheduleSelf() {
    686         Drawable mockDrawable = new MockDrawable();
    687         mockDrawable.scheduleSelf(null, 1000L);
    688 
    689         Runnable runnable = mock(Runnable.class);
    690         mockDrawable.scheduleSelf(runnable, 1000L);
    691 
    692         Callback mockCallback = mock(Callback.class);
    693         mockDrawable.setCallback(mockCallback);
    694         mockDrawable.scheduleSelf(runnable, 1000L);
    695         verify(mockCallback).scheduleDrawable(eq(mockDrawable), eq(runnable), eq(1000L));
    696 
    697         mockDrawable.scheduleSelf(runnable, 0L);
    698         verify(mockCallback).scheduleDrawable(eq(mockDrawable), eq(runnable), eq(0L));
    699 
    700         mockDrawable.scheduleSelf(runnable, -1000L);
    701         verify(mockCallback).scheduleDrawable(eq(mockDrawable), eq(runnable), eq(-1000L));
    702     }
    703 
    704     @Test
    705     public void testAccessCallback() {
    706         Drawable mockDrawable = new MockDrawable();
    707         Callback mockCallback = mock(Callback.class);
    708 
    709         mockDrawable.setCallback(mockCallback);
    710         assertEquals(mockCallback, mockDrawable.getCallback());
    711 
    712         mockDrawable.setCallback(null);
    713         assertEquals(null, mockDrawable.getCallback());
    714     }
    715 
    716     @Test
    717     public void testSetColorFilter() {
    718         Drawable mockDrawable = new MockDrawable();
    719         mockDrawable.setColorFilter(5, PorterDuff.Mode.CLEAR);
    720     }
    721 
    722     @Test
    723     public void testSetDither() {
    724         Drawable mockDrawable = new MockDrawable();
    725 
    726         // No-op in the Drawable superclass.
    727         mockDrawable.setDither(false);
    728     }
    729 
    730     @Test
    731     public void testSetHotspotBounds() {
    732         Drawable mockDrawable = new MockDrawable();
    733 
    734         // No-op in the Drawable superclass.
    735         mockDrawable.setHotspotBounds(10, 15, 100, 150);
    736     }
    737 
    738     @Test
    739     public void testGetHotspotBounds() {
    740         Drawable mockDrawable = new MockDrawable();
    741 
    742         // No-op in the Drawable superclass.
    743         mockDrawable.getHotspotBounds(new Rect());
    744     }
    745 
    746     @Test
    747     public void testAccessLayoutDirection() {
    748         Drawable mockDrawable = new MockDrawable();
    749 
    750         mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
    751         assertEquals(View.LAYOUT_DIRECTION_LTR, mockDrawable.getLayoutDirection());
    752 
    753         mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
    754         assertEquals(View.LAYOUT_DIRECTION_RTL, mockDrawable.getLayoutDirection());
    755     }
    756 
    757     @Test
    758     public void testOnLayoutDirectionChanged() {
    759         Drawable mockDrawable = new MockDrawable();
    760 
    761         // No-op in the Drawable superclass.
    762         mockDrawable.onLayoutDirectionChanged(View.LAYOUT_DIRECTION_LTR);
    763     }
    764 
    765     @Test
    766     public void testSetFilterBitmap() {
    767         Drawable mockDrawable = new MockDrawable();
    768 
    769         // No-op in the Drawable superclass.
    770         mockDrawable.setFilterBitmap(false);
    771     }
    772 
    773     @Test
    774     public void testIsFilterBitmap() {
    775         Drawable mockDrawable = new MockDrawable();
    776 
    777         // No-op in the Drawable superclass.
    778         mockDrawable.isFilterBitmap();
    779     }
    780 
    781     @Test
    782     public void testUnscheduleSelf() {
    783         Drawable mockDrawable = new MockDrawable();
    784         Drawable.Callback mockCallback = mock(Drawable.Callback.class);
    785         mockDrawable.setCallback(mockCallback);
    786         mockDrawable.unscheduleSelf(null);
    787         verify(mockCallback, times(1)).unscheduleDrawable(mockDrawable, null);
    788     }
    789 
    790     @Test
    791     public void testMutate() {
    792         Drawable mockDrawable = new MockDrawable();
    793         assertSame(mockDrawable, mockDrawable.mutate());
    794     }
    795 
    796     @Test
    797     public void testDefaultOpticalInsetsIsNone() {
    798         Drawable mockDrawable = new MockDrawable();
    799         assertEquals(Insets.NONE, mockDrawable.getOpticalInsets());
    800     }
    801 
    802     @Test
    803     public void testNoSetTintModeInfiniteLoop() {
    804         // Setting a PorterDuff.Mode should delegate to the BlendMode API
    805         TestTintDrawable testTintDrawable = new TestTintDrawable();
    806 
    807         testTintDrawable.setTintMode(PorterDuff.Mode.OVERLAY);
    808         assertEquals(2, testTintDrawable.getNumTimesTintModeInvoked());
    809         assertEquals(1, testTintDrawable.getNumTimesBlendModeInvoked());
    810     }
    811 
    812     @Test
    813     public void testPorterDuffTintWithUnsupportedBlendMode() {
    814         // Setting a BlendMode should delegate to the PorterDuff.Mode API
    815         TestTintDrawable testTintDrawable = new TestTintDrawable();
    816         testTintDrawable.setTintBlendMode(BlendMode.LUMINOSITY);
    817         // 1 time invoking setTintBlendMode because the default is applied if
    818         // there is no equivalent for the luminosity blend mode on older API levels
    819         assertEquals(1, testTintDrawable.getNumTimesTintModeInvoked());
    820         assertEquals(2, testTintDrawable.getNumTimesBlendModeInvoked());
    821     }
    822 
    823     @Test
    824     public void testPorterDuffTintWithSupportedBlendMode() {
    825         TestTintDrawable testTintDrawable = new TestTintDrawable();
    826         testTintDrawable.setTintBlendMode(BlendMode.SRC_OVER);
    827         assertEquals(1, testTintDrawable.getNumTimesTintModeInvoked());
    828         assertEquals(2, testTintDrawable.getNumTimesBlendModeInvoked());
    829     }
    830 
    831     @Test
    832     public void testBlendModeImplementationInvokedWithBlendMode() {
    833         TestBlendModeImplementedDrawable test = new TestBlendModeImplementedDrawable();
    834 
    835         test.setTintBlendMode(BlendMode.SRC);
    836         assertEquals(1, test.getNumTimesBlendModeInvoked());
    837         assertEquals(0, test.getNumTimesTintModeInvoked());
    838     }
    839 
    840     @Test
    841     public void testBlendModeImplementationInvokedWithPorterDuffMode() {
    842         TestBlendModeImplementedDrawable test = new TestBlendModeImplementedDrawable();
    843         test.setTintMode(PorterDuff.Mode.CLEAR);
    844 
    845         assertEquals(1, test.getNumTimesTintModeInvoked());
    846         assertEquals(1, test.getNumTimesBlendModeInvoked());
    847     }
    848 
    849     @Test
    850     public void testPorterDuffImplementationInvokedWithBlendMode() {
    851         TestPorterDuffImplementedDrawable test = new TestPorterDuffImplementedDrawable();
    852         test.setTintBlendMode(BlendMode.CLEAR);
    853 
    854         assertEquals(1, test.getNumTimesBlendModeInvoked());
    855         assertEquals(1, test.getNumTimesTintModeInvoked());
    856     }
    857 
    858     @Test
    859     public void testPorterDuffImplementationInvokedWithPorterDuffMode() {
    860         TestPorterDuffImplementedDrawable test = new TestPorterDuffImplementedDrawable();
    861         test.setTintMode(PorterDuff.Mode.CLEAR);
    862 
    863         assertEquals(1, test.getNumTimesTintModeInvoked());
    864         assertEquals(0, test.getNumTimesBlendModeInvoked());
    865     }
    866 
    867     @Test
    868     public void testNullPorterDuffReturnsDefaultBlendMode() {
    869         TestNullBlendModeDrawable d = new TestNullBlendModeDrawable();
    870         d.setTintMode((PorterDuff.Mode) null);
    871         assertEquals(BlendMode.SRC_IN, d.mode);
    872     }
    873 
    874     @Test
    875     public void testNullBlendModeReturnsDefaultPorterDuffMode() {
    876         TestNullPorterDuffDrawable d = new TestNullPorterDuffDrawable();
    877         d.setTintBlendMode((BlendMode) null);
    878         assertEquals(PorterDuff.Mode.SRC_IN, d.mode);
    879     }
    880 
    881     // Since Mockito can't mock or spy on protected methods, we have a custom extension
    882     // of Drawable to track calls to protected methods. This class also has empty implementations
    883     // of the base abstract methods.
    884     private static class MockDrawable extends Drawable {
    885         private ColorFilter mColorFilter;
    886 
    887         @Override
    888         public void draw(Canvas canvas) {
    889         }
    890 
    891         @Override
    892         public void setAlpha(int alpha) {
    893         }
    894 
    895         @Override
    896         public void setColorFilter(ColorFilter cf) {
    897             mColorFilter = cf;
    898         }
    899 
    900         @Override
    901         public ColorFilter getColorFilter() {
    902             return mColorFilter;
    903         }
    904 
    905         @Override
    906         public int getOpacity() {
    907             return PixelFormat.OPAQUE;
    908         }
    909 
    910         protected void onBoundsChange(Rect bounds) {
    911             super.onBoundsChange(bounds);
    912         }
    913 
    914         protected boolean onLevelChange(int level) {
    915             return super.onLevelChange(level);
    916         }
    917 
    918         protected boolean onStateChange(int[] state) {
    919             return super.onStateChange(state);
    920         }
    921     }
    922 
    923     private static class TestTintDrawable extends Drawable {
    924 
    925         private int mSetTintModeInvoked = 0;
    926         private int mSetBlendModeInvoked = 0;
    927 
    928         @Override
    929         public void draw(Canvas canvas) {
    930 
    931         }
    932 
    933         @Override
    934         public void setAlpha(int alpha) {
    935 
    936         }
    937 
    938         @Override
    939         public void setColorFilter(ColorFilter colorFilter) {
    940 
    941         }
    942 
    943         @Override
    944         public int getOpacity() {
    945             return 0;
    946         }
    947 
    948         @Override
    949         public void setTintMode(PorterDuff.Mode tintMode) {
    950             mSetTintModeInvoked++;
    951             super.setTintMode(tintMode);
    952         }
    953 
    954         @Override
    955         public void setTintBlendMode(BlendMode blendMode) {
    956             mSetBlendModeInvoked++;
    957             super.setTintBlendMode(blendMode);
    958         }
    959 
    960         public int getNumTimesTintModeInvoked() {
    961             return mSetTintModeInvoked;
    962         }
    963 
    964         public int getNumTimesBlendModeInvoked() {
    965             return mSetBlendModeInvoked;
    966         }
    967     }
    968 
    969     private static class TestNullPorterDuffDrawable extends Drawable {
    970 
    971         public PorterDuff.Mode mode;
    972 
    973         @Override
    974         public void draw(Canvas canvas) {
    975 
    976         }
    977 
    978         @Override
    979         public void setAlpha(int alpha) {
    980 
    981         }
    982 
    983         @Override
    984         public void setColorFilter(ColorFilter colorFilter) {
    985 
    986         }
    987 
    988         @Override
    989         public void setTintMode(PorterDuff.Mode tintMode) {
    990             mode = tintMode;
    991         }
    992 
    993         @Override
    994         public int getOpacity() {
    995             return 0;
    996         }
    997     }
    998 
    999     private static class TestNullBlendModeDrawable extends Drawable {
   1000 
   1001         public BlendMode mode;
   1002 
   1003         @Override
   1004         public void draw(Canvas canvas) {
   1005 
   1006         }
   1007 
   1008         @Override
   1009         public void setAlpha(int alpha) {
   1010 
   1011         }
   1012 
   1013         @Override
   1014         public void setColorFilter(ColorFilter colorFilter) {
   1015 
   1016         }
   1017 
   1018         @Override
   1019         public void setTintBlendMode(BlendMode blendMode) {
   1020             mode = blendMode;
   1021         }
   1022 
   1023         @Override
   1024         public int getOpacity() {
   1025             return 0;
   1026         }
   1027     }
   1028 
   1029     private static class TestBlendModeImplementedDrawable extends Drawable {
   1030 
   1031         private int mSetTintModeInvoked = 0;
   1032         private int mSetBlendModeInvoked = 0;
   1033 
   1034         @Override
   1035         public void draw(Canvas canvas) {
   1036 
   1037         }
   1038 
   1039         @Override
   1040         public void setAlpha(int alpha) {
   1041 
   1042         }
   1043 
   1044         @Override
   1045         public void setColorFilter(ColorFilter colorFilter) {
   1046 
   1047         }
   1048 
   1049         @Override
   1050         public int getOpacity() {
   1051             return 0;
   1052         }
   1053 
   1054         @Override
   1055         public void setTintMode(PorterDuff.Mode tintMode) {
   1056             mSetTintModeInvoked++;
   1057             super.setTintMode(tintMode);
   1058         }
   1059 
   1060         @Override
   1061         public void setTintBlendMode(BlendMode blendMode) {
   1062             // Intentionally not delegating to super class implementation
   1063             mSetBlendModeInvoked++;
   1064         }
   1065 
   1066         public int getNumTimesTintModeInvoked() {
   1067             return mSetTintModeInvoked;
   1068         }
   1069 
   1070         public int getNumTimesBlendModeInvoked() {
   1071             return mSetBlendModeInvoked;
   1072         }
   1073     }
   1074 
   1075     private static class TestPorterDuffImplementedDrawable extends Drawable {
   1076 
   1077         private int mSetTintModeInvoked = 0;
   1078         private int mSetBlendModeInvoked = 0;
   1079 
   1080         @Override
   1081         public void draw(Canvas canvas) {
   1082 
   1083         }
   1084 
   1085         @Override
   1086         public void setAlpha(int alpha) {
   1087 
   1088         }
   1089 
   1090         @Override
   1091         public void setColorFilter(ColorFilter colorFilter) {
   1092 
   1093         }
   1094 
   1095         @Override
   1096         public int getOpacity() {
   1097             return 0;
   1098         }
   1099 
   1100         @Override
   1101         public void setTintMode(PorterDuff.Mode tintMode) {
   1102             // Intentionally not delegating to super class implementation
   1103             mSetTintModeInvoked++;
   1104         }
   1105 
   1106         @Override
   1107         public void setTintBlendMode(BlendMode blendMode) {
   1108             mSetBlendModeInvoked++;
   1109             super.setTintBlendMode(blendMode);
   1110         }
   1111 
   1112         public int getNumTimesTintModeInvoked() {
   1113             return mSetTintModeInvoked;
   1114         }
   1115 
   1116         public int getNumTimesBlendModeInvoked() {
   1117             return mSetBlendModeInvoked;
   1118         }
   1119     }
   1120 }
   1121