1 /*
2  * Copyright (C) 2015 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.assertSame;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Matchers.any;
24 import static org.mockito.Matchers.anyBoolean;
25 import static org.mockito.Matchers.anyInt;
26 import static org.mockito.Matchers.anyLong;
27 import static org.mockito.Mockito.doNothing;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.reset;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 
35 import android.graphics.Canvas;
36 import android.graphics.Color;
37 import android.graphics.ColorFilter;
38 import android.graphics.Insets;
39 import android.graphics.PixelFormat;
40 import android.graphics.PorterDuff;
41 import android.graphics.PorterDuffColorFilter;
42 import android.graphics.Rect;
43 import android.graphics.cts.R;
44 import android.graphics.drawable.BitmapDrawable;
45 import android.graphics.drawable.ColorDrawable;
46 import android.graphics.drawable.Drawable;
47 import android.graphics.drawable.DrawableWrapper;
48 import android.util.StateSet;
49 
50 import androidx.annotation.Nullable;
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.SmallTest;
53 import androidx.test.runner.AndroidJUnit4;
54 
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 
58 import java.util.Arrays;
59 
60 @SmallTest
61 @RunWith(AndroidJUnit4.class)
62 public class DrawableWrapperTest {
63     static class MyWrapper extends DrawableWrapper {
MyWrapper(Drawable dr)64         public MyWrapper(Drawable dr) {
65             super(dr);
66         }
67     }
68 
69     @SuppressWarnings("deprecation")
70     @Test
testConstructor()71     public void testConstructor() {
72         Drawable d = new BitmapDrawable();
73         DrawableWrapper wrapper = new MyWrapper(d);
74         assertSame(d, wrapper.getDrawable());
75 
76         new MyWrapper(null);
77     }
78 
79     @SuppressWarnings("deprecation")
80     @Test
testGetDrawable()81     public void testGetDrawable() {
82         Drawable d = new BitmapDrawable();
83         DrawableWrapper wrapper = new MyWrapper(d);
84         assertSame(d, wrapper.getDrawable());
85     }
86 
87     @SuppressWarnings("deprecation")
88     @Test
testSetDrawable()89     public void testSetDrawable() {
90         Drawable d = new BitmapDrawable();
91         DrawableWrapper wrapper = new MyWrapper(null);
92         assertSame(null, wrapper.getDrawable());
93 
94         wrapper.setDrawable(d);
95         assertSame(d, wrapper.getDrawable());
96     }
97 
98     @SuppressWarnings("deprecation")
99     @Test
testInvalidateDrawable()100     public void testInvalidateDrawable() {
101         DrawableWrapper wrapper = new MyWrapper(new BitmapDrawable());
102 
103         Drawable.Callback cb = mock(Drawable.Callback.class);
104         wrapper.setCallback(cb);
105         wrapper.invalidateDrawable(null);
106         verify(cb, times(1)).invalidateDrawable(any());
107 
108         reset(cb);
109         wrapper.invalidateDrawable(new BitmapDrawable());
110         verify(cb, times(1)).invalidateDrawable(any());
111 
112         reset(cb);
113         wrapper.setCallback(null);
114         wrapper.invalidateDrawable(null);
115         verify(cb, never()).invalidateDrawable(any());
116     }
117 
118     @SuppressWarnings("deprecation")
119     @Test
testScheduleDrawable()120     public void testScheduleDrawable() {
121         DrawableWrapper wrapper = new MyWrapper(new BitmapDrawable());
122 
123         Drawable.Callback cb = mock(Drawable.Callback.class);
124         wrapper.setCallback(cb);
125         wrapper.scheduleDrawable(null, null, 0);
126         verify(cb, times(1)).scheduleDrawable(any(), any(), anyLong());
127 
128         reset(cb);
129         wrapper.scheduleDrawable(new BitmapDrawable(), () -> {}, 1000L);
130         verify(cb, times(1)).scheduleDrawable(any(), any(), anyLong());
131 
132         reset(cb);
133         wrapper.setCallback(null);
134         wrapper.scheduleDrawable(null, null, 0);
135         verify(cb, never()).scheduleDrawable(any(), any(), anyLong());
136     }
137 
138     @SuppressWarnings("deprecation")
139     @Test
testUnscheduleDrawable()140     public void testUnscheduleDrawable() {
141         DrawableWrapper wrapper = new MyWrapper(new BitmapDrawable());
142 
143         Drawable.Callback cb = mock(Drawable.Callback.class);
144         wrapper.setCallback(cb);
145         wrapper.unscheduleDrawable(null, null);
146         verify(cb, times(1)).unscheduleDrawable(any(), any());
147 
148         reset(cb);
149         wrapper.unscheduleDrawable(new BitmapDrawable(), () -> {});
150         verify(cb, times(1)).unscheduleDrawable(any(), any());
151 
152         reset(cb);
153         wrapper.setCallback(null);
154         wrapper.unscheduleDrawable(null, null);
155         verify(cb, never()).unscheduleDrawable(any(), any());
156     }
157 
158     @Test
testCallbackIsSet()159     public void testCallbackIsSet() {
160         Drawable dr = new MockDrawable();
161 
162         DrawableWrapper wrapper = new MyWrapper(dr);
163         assertEquals(wrapper, dr.getCallback());
164     }
165 
166     @Test
testDraw()167     public void testDraw() {
168         Drawable mockDrawable = spy(new ColorDrawable(Color.BLUE));
169         doNothing().when(mockDrawable).draw(any());
170         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
171 
172         wrapper.draw(new Canvas());
173         verify(mockDrawable, times(1)).draw(any());
174 
175         reset(mockDrawable);
176         doNothing().when(mockDrawable).draw(any());
177         wrapper.draw(null);
178         verify(mockDrawable, times(1)).draw(any());
179     }
180 
181     @Test
testGetChangingConfigurations()182     public void testGetChangingConfigurations() {
183         final int SUPER_CONFIG = 1;
184         final int CONTAINED_DRAWABLE_CONFIG = 2;
185 
186         MockDrawable mockDrawable = new MockDrawable();
187         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
188 
189         assertEquals(0, wrapper.getChangingConfigurations());
190 
191         mockDrawable.setChangingConfigurations(CONTAINED_DRAWABLE_CONFIG);
192         assertEquals(CONTAINED_DRAWABLE_CONFIG, wrapper.getChangingConfigurations());
193 
194         wrapper.setChangingConfigurations(SUPER_CONFIG);
195         assertEquals(SUPER_CONFIG | CONTAINED_DRAWABLE_CONFIG,
196                 wrapper.getChangingConfigurations());
197     }
198 
199     @Test
testGetPadding()200     public void testGetPadding() {
201         Drawable mockDrawable = spy(new ColorDrawable(Color.RED));
202         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
203 
204         // this method will call contained drawable's getPadding method.
205         wrapper.getPadding(new Rect());
206         verify(mockDrawable, times(1)).getPadding(any());
207     }
208 
209     @Test
testColorFilter()210     public void testColorFilter() {
211         Drawable wrappedDrawable = new MockDrawable();
212         DrawableWrapper wrapper = new MyWrapper(wrappedDrawable);
213         PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(Color.BLUE,
214                 PorterDuff.Mode.SRC_OVER);
215         wrapper.setColorFilter(colorFilter);
216         ColorFilter obtainedColorFilter = wrapper.getColorFilter();
217         assertEquals("Color filters are not properly returned", colorFilter, obtainedColorFilter);
218     }
219 
220     @Test(expected=NullPointerException.class)
testGetPaddingNull()221     public void testGetPaddingNull() {
222         DrawableWrapper wrapper = new MyWrapper(new ColorDrawable(Color.RED));
223 
224         wrapper.getPadding(null);
225     }
226 
227     @Test
testSetVisible()228     public void testSetVisible() {
229         Drawable mockDrawable = spy(new ColorDrawable(Color.YELLOW));
230         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
231         assertTrue(wrapper.isVisible());
232 
233         reset(mockDrawable);
234         assertTrue(wrapper.setVisible(false, false));
235         assertFalse(wrapper.isVisible());
236         verify(mockDrawable, times(1)).setVisible(anyBoolean(), anyBoolean());
237 
238         reset(mockDrawable);
239         assertFalse(wrapper.setVisible(false, false));
240         assertFalse(wrapper.isVisible());
241         verify(mockDrawable, times(1)).setVisible(anyBoolean(), anyBoolean());
242 
243         reset(mockDrawable);
244         assertTrue(wrapper.setVisible(true, false));
245         assertTrue(wrapper.isVisible());
246         verify(mockDrawable, times(1)).setVisible(anyBoolean(), anyBoolean());
247     }
248 
249     @Test
testSetAlpha()250     public void testSetAlpha() {
251         Drawable mockDrawable = spy(new ColorDrawable(Color.MAGENTA));
252         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
253 
254         // this method will call contained drawable's setAlpha method.
255         wrapper.setAlpha(100);
256         verify(mockDrawable, times(1)).setAlpha(anyInt());
257 
258         reset(mockDrawable);
259         wrapper.setAlpha(Integer.MAX_VALUE);
260         verify(mockDrawable, times(1)).setAlpha(anyInt());
261 
262         reset(mockDrawable);
263         wrapper.setAlpha(-1);
264         verify(mockDrawable, times(1)).setAlpha(anyInt());
265     }
266 
267     @Test
testSetColorFilter()268     public void testSetColorFilter() {
269         Drawable mockDrawable = spy(new ColorDrawable(Color.GRAY));
270         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
271 
272         // this method will call contained drawable's setColorFilter method.
273         wrapper.setColorFilter(new ColorFilter());
274         verify(mockDrawable, times(1)).setColorFilter(any());
275 
276         reset(mockDrawable);
277         wrapper.setColorFilter(null);
278         verify(mockDrawable, times(1)).setColorFilter(any());
279     }
280 
281     @Test
testGetOpacity()282     public void testGetOpacity() {
283         Drawable mockDrawable = spy(new ColorDrawable(Color.RED));
284         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
285 
286         // This method will call contained drawable's getOpacity method.
287         wrapper.setLevel(1);
288         wrapper.getOpacity();
289         verify(mockDrawable, times(1)).getOpacity();
290     }
291 
292     @Test
testIsStateful()293     public void testIsStateful() {
294         Drawable mockDrawable = spy(new ColorDrawable(Color.BLACK));
295         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
296 
297         // this method will call contained drawable's isStateful method.
298         wrapper.isStateful();
299         verify(mockDrawable, times(1)).isStateful();
300     }
301 
302     @Test
testOnStateChange()303     public void testOnStateChange() {
304         Drawable d = new MockDrawable();
305         MockDrawableWrapper wrapper = new MockDrawableWrapper(d);
306         assertEquals("initial child state is empty", d.getState(), StateSet.WILD_CARD);
307 
308         int[] state = new int[] {1, 2, 3};
309         assertFalse("child did not change", wrapper.onStateChange(state));
310         assertEquals("child state did not change", d.getState(), StateSet.WILD_CARD);
311 
312         d = InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.statelistdrawable);
313         wrapper = new MockDrawableWrapper(d);
314         assertEquals("initial child state is empty", d.getState(), StateSet.WILD_CARD);
315         wrapper.onStateChange(state);
316         assertTrue("child state changed", Arrays.equals(state, d.getState()));
317 
318         // input null as param
319         wrapper.onStateChange(null);
320         // expected, no Exception thrown out, test success
321     }
322 
323     @Test
testOnLevelChange()324     public void testOnLevelChange() {
325         MockDrawable mockDrawable = new MockDrawable();
326         MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
327 
328         assertEquals(0, mockDrawable.getLevel());
329         assertFalse(mockDrawableWrapper.onLevelChange(0));
330         assertFalse(mockDrawable.hasCalledOnLevelChange());
331 
332         assertFalse(mockDrawableWrapper.onLevelChange(1000));
333         assertTrue(mockDrawable.hasCalledOnLevelChange());
334         assertEquals(1000, mockDrawable.getLevel());
335 
336         mockDrawable.reset();
337         assertFalse(mockDrawableWrapper.onLevelChange(Integer.MIN_VALUE));
338         assertTrue(mockDrawable.hasCalledOnLevelChange());
339     }
340 
341     @Test
testOnBoundsChange()342     public void testOnBoundsChange() {
343         MockDrawable mockDrawable = new MockDrawable();
344         MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
345         Rect bounds = new Rect(2, 2, 26, 32);
346         mockDrawable.setBounds(bounds);
347         mockDrawableWrapper.onBoundsChange(bounds);
348 
349         mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
350         mockDrawable.setBounds(bounds);
351         mockDrawableWrapper.onBoundsChange(bounds);
352         assertEquals(bounds.left, mockDrawable.getBounds().left);
353         assertEquals(bounds.top, mockDrawable.getBounds().top);
354         assertEquals(bounds.right, mockDrawable.getBounds().right);
355         assertEquals(bounds.bottom, mockDrawable.getBounds().bottom);
356 
357         bounds = mockDrawable.getBounds();
358         assertEquals(2, bounds.left);
359         assertEquals(2, bounds.top);
360         assertEquals(26, bounds.right);
361         assertEquals(32, bounds.bottom);
362     }
363 
364     @Test(expected=NullPointerException.class)
testOnBoundsChangeNull()365     public void testOnBoundsChangeNull() {
366         MockDrawable mockDrawable = new MockDrawable();
367         MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
368 
369         mockDrawableWrapper.onBoundsChange(null);
370     }
371 
372     @Test
testGetIntrinsicWidth()373     public void testGetIntrinsicWidth() {
374         Drawable mockDrawable = spy(new ColorDrawable(Color.WHITE));
375         MyWrapper wrapper = new MyWrapper(mockDrawable);
376 
377         // this method will call contained drawable's getIntrinsicWidth method.
378         wrapper.getIntrinsicWidth();
379         verify(mockDrawable, times(1)).getIntrinsicWidth();
380     }
381 
382     @Test
testGetIntrinsicHeight()383     public void testGetIntrinsicHeight() {
384         Drawable mockDrawable = spy(new ColorDrawable(Color.RED));
385         DrawableWrapper wrapper = new MyWrapper(mockDrawable);
386 
387         // this method will call contained drawable's getIntrinsicHeight method.
388         wrapper.getIntrinsicHeight();
389         verify(mockDrawable, times(1)).getIntrinsicHeight();
390     }
391 
392     @Test
testGetOpticalInsetsNoInternalDrawable()393     public void testGetOpticalInsetsNoInternalDrawable() {
394         DrawableWrapper wrapper = new MockDrawableWrapper(null);
395         assertEquals(Insets.NONE, wrapper.getOpticalInsets());
396     }
397 
398     @Test
testGetOpticalInsetsFromInternalDrawable()399     public void testGetOpticalInsetsFromInternalDrawable() {
400         MockDrawable drawable = new MockDrawable();
401         drawable.setInsets(Insets.of(30, 60, 90, 120));
402         DrawableWrapper wrapper = new MockDrawableWrapper(drawable);
403 
404         assertEquals(Insets.of(30, 60, 90, 120), wrapper.getOpticalInsets());
405     }
406 
407     @SuppressWarnings("deprecation")
408     @Test
testGetConstantState()409     public void testGetConstantState() {
410         DrawableWrapper wrapper = new MyWrapper(new BitmapDrawable());
411         wrapper.getConstantState();
412     }
413 
414     @Test
testJumpToCurrentStateInvoked()415     public void testJumpToCurrentStateInvoked() {
416         MockDrawable inner = new MockDrawable();
417         DrawableWrapper wrapper = new MockDrawableWrapper(inner);
418 
419         wrapper.jumpToCurrentState();
420         assertTrue(inner.isJumpToCurrentStateInvoked());
421     }
422 
423     // Since Mockito can't mock or spy on protected methods, we have a custom extension
424     // of Drawable to track calls to protected methods. This class also has empty implementations
425     // of the base abstract methods.
426     private static class MockDrawable extends Drawable {
427         private boolean mCalledOnLevelChange = false;
428         private ColorFilter mColorFilter;
429         private Insets mInsets = null;
430 
431         private boolean mJumpToCurrentStateInvoked = false;
432 
isJumpToCurrentStateInvoked()433         public boolean isJumpToCurrentStateInvoked() {
434             return mJumpToCurrentStateInvoked;
435         }
436 
437         @Override
jumpToCurrentState()438         public void jumpToCurrentState() {
439             mJumpToCurrentStateInvoked = true;
440         }
441 
442         @Override
draw(Canvas canvas)443         public void draw(Canvas canvas) {
444         }
445 
446         @Override
getOpacity()447         public int getOpacity() {
448             return PixelFormat.OPAQUE;
449         }
450 
451         @Override
setAlpha(int alpha)452         public void setAlpha(int alpha) {
453         }
454 
455         @Override
setColorFilter(ColorFilter cf)456         public void setColorFilter(ColorFilter cf) {
457             mColorFilter = cf;
458         }
459 
460         @Override
getColorFilter()461         public ColorFilter getColorFilter() {
462             return mColorFilter;
463         }
464 
setInsets(@ullable Insets insets)465         public void setInsets(@Nullable Insets insets) {
466             mInsets = insets;
467         }
468 
469         @Override
getOpticalInsets()470         public Insets getOpticalInsets() {
471             return mInsets != null ? mInsets : Insets.NONE;
472         }
473 
474         @Override
onLevelChange(int level)475         protected boolean onLevelChange(int level) {
476             mCalledOnLevelChange = true;
477             return super.onLevelChange(level);
478         }
479 
hasCalledOnLevelChange()480         public boolean hasCalledOnLevelChange() {
481             return mCalledOnLevelChange;
482         }
483 
reset()484         public void reset() {
485             mCalledOnLevelChange = false;
486         }
487     }
488 
489     private static class MockDrawableWrapper extends DrawableWrapper {
MockDrawableWrapper()490         MockDrawableWrapper() {
491             super(null);
492         }
493 
MockDrawableWrapper(Drawable drawable)494         public MockDrawableWrapper(Drawable drawable) {
495             super(drawable);
496         }
497 
498         @Override
onStateChange(int[] state)499         protected boolean onStateChange(int[] state) {
500             return super.onStateChange(state);
501         }
502 
503         @Override
onLevelChange(int level)504         protected boolean onLevelChange(int level) {
505             return super.onLevelChange(level);
506         }
507 
508         @Override
onBoundsChange(Rect bounds)509         protected void onBoundsChange(Rect bounds) {
510             super.onBoundsChange(bounds);
511         }
512     }
513 }
514