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.cts;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotEquals;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.Color;
28 import android.graphics.Matrix;
29 import android.graphics.Paint;
30 import android.graphics.Path;
31 import android.graphics.Path.Direction;
32 import android.graphics.PathIterator;
33 import android.graphics.RectF;
34 import android.platform.test.annotations.DisabledOnRavenwood;
35 import android.platform.test.ravenwood.RavenwoodRule;
36 
37 import androidx.test.filters.SmallTest;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import com.android.compatibility.common.util.ApiTest;
41 
42 import org.junit.Rule;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 
46 @SmallTest
47 @RunWith(AndroidJUnit4.class)
48 public class PathTest {
49     @Rule
50     public final RavenwoodRule mRavenwood = new RavenwoodRule();
51 
52     // Test constants
53     private static final float LEFT = 10.0f;
54     private static final float RIGHT = 50.0f;
55     private static final float TOP = 10.0f;
56     private static final float BOTTOM = 50.0f;
57     private static final float XCOORD = 40.0f;
58     private static final float YCOORD = 40.0f;
59     private static final int SQUARE = 10;
60     private static final int WIDTH = 100;
61     private static final int HEIGHT = 100;
62     private static final int START_X = 10;
63     private static final int START_Y = 20;
64     private static final int OFFSET_X = 30;
65     private static final int OFFSET_Y = 40;
66 
67     @Test
testConstructor()68     public void testConstructor() {
69         // new the Path instance
70         new Path();
71 
72         // another the Path instance with different params
73         new Path(new Path());
74     }
75 
76     @Test
testAddRect1()77     public void testAddRect1() {
78         Path path = new Path();
79         assertTrue(path.isEmpty());
80         RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
81         path.addRect(rect, Path.Direction.CW);
82         assertFalse(path.isEmpty());
83     }
84 
85     @Test
testAddRect2()86     public void testAddRect2() {
87         Path path = new Path();
88         assertTrue(path.isEmpty());
89         path.addRect(LEFT, TOP, RIGHT, BOTTOM, Path.Direction.CW);
90         assertFalse(path.isEmpty());
91     }
92 
93     @Test
testMoveTo()94     public void testMoveTo() {
95         Path path = new Path();
96         path.moveTo(10.0f, 10.0f);
97     }
98 
99     @Test
100     @DisabledOnRavenwood(blockedBy = {Paint.class})
testSet()101     public void testSet() {
102         Path path = new Path();
103         assertTrue(path.isEmpty());
104         Path path1 = new Path();
105         addRectToPath(path1);
106         path.set(path1);
107         verifyPathsAreEquivalent(path, path1);
108     }
109 
110     @Test
111     @DisabledOnRavenwood(blockedBy = {Paint.class})
testSetCleanOld()112     public void testSetCleanOld() {
113         Path path = new Path();
114         addRectToPath(path);
115         path.addRect(new RectF(0, 0, 10, 10), Path.Direction.CW);
116         Path path1 = new Path();
117         path1.addRect(new RectF(10, 10, 20, 20), Path.Direction.CW);
118         path.set(path1);
119         verifyPathsAreEquivalent(path, path1);
120     }
121 
122     @Test
123     @DisabledOnRavenwood(blockedBy = {Paint.class})
testSetEmptyPath()124     public void testSetEmptyPath() {
125         Path path = new Path();
126         addRectToPath(path);
127         Path path1 = new Path();
128         path.set(path1);
129         verifyPathsAreEquivalent(path, path1);
130     }
131 
132     @Test
testAccessFillType()133     public void testAccessFillType() {
134         // set the expected value
135         Path.FillType expected1 = Path.FillType.EVEN_ODD;
136         Path.FillType expected2 = Path.FillType.INVERSE_EVEN_ODD;
137         Path.FillType expected3 = Path.FillType.INVERSE_WINDING;
138         Path.FillType expected4 = Path.FillType.WINDING;
139 
140         // new the Path instance
141         Path path = new Path();
142         // set FillType by {@link Path#setFillType(FillType)}
143         path.setFillType(Path.FillType.EVEN_ODD);
144         assertEquals(expected1, path.getFillType());
145         path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
146         assertEquals(expected2, path.getFillType());
147         path.setFillType(Path.FillType.INVERSE_WINDING);
148         assertEquals(expected3, path.getFillType());
149         path.setFillType(Path.FillType.WINDING);
150         assertEquals(expected4, path.getFillType());
151     }
152 
153     @Test
testRQuadTo()154     public void testRQuadTo() {
155         Path path = new Path();
156         assertTrue(path.isEmpty());
157         path.rQuadTo(5.0f, 5.0f, 10.0f, 10.0f);
158         assertFalse(path.isEmpty());
159     }
160 
161     @Test
testTransform1()162     public void testTransform1() {
163         Path path = new Path();
164         assertTrue(path.isEmpty());
165         Path dst = new Path();
166         addRectToPath(path);
167         path.transform(new Matrix(), dst);
168         assertFalse(dst.isEmpty());
169     }
170 
171     @Test
testLineTo()172     public void testLineTo() {
173         Path path = new Path();
174         assertTrue(path.isEmpty());
175         path.lineTo(XCOORD, YCOORD);
176         assertFalse(path.isEmpty());
177     }
178 
179     @Test
testClose()180     public void testClose() {
181         Path path = new Path();
182         assertTrue(path.isEmpty());
183         addRectToPath(path);
184         path.close();
185     }
186 
187     @Test
testQuadTo()188     public void testQuadTo() {
189         Path path = new Path();
190         assertTrue(path.isEmpty());
191         path.quadTo(20.0f, 20.0f, 40.0f, 40.0f);
192         assertFalse(path.isEmpty());
193     }
194 
195     @Test
testAddCircle()196     public void testAddCircle() {
197         // new the Path instance
198         Path path = new Path();
199         assertTrue(path.isEmpty());
200         path.addCircle(XCOORD, YCOORD, 10.0f, Path.Direction.CW);
201         assertFalse(path.isEmpty());
202     }
203 
204     @Test
testArcTo1()205     public void testArcTo1() {
206         Path path = new Path();
207         assertTrue(path.isEmpty());
208         RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
209         path.arcTo(oval, 0.0f, 30.0f, true);
210         assertFalse(path.isEmpty());
211     }
212 
213     @Test
testArcTo2()214     public void testArcTo2() {
215         Path path = new Path();
216         assertTrue(path.isEmpty());
217         RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
218         path.arcTo(oval, 0.0f, 30.0f);
219         assertFalse(path.isEmpty());
220     }
221 
222     @Test
testDeprecatedComputeBounds1()223     public void testDeprecatedComputeBounds1() {
224         RectF expected = new RectF(0.0f, 0.0f, 0.0f, 0.0f);
225         Path path = new Path();
226         assertTrue(path.isEmpty());
227         RectF bounds = new RectF();
228         path.computeBounds(bounds, true);
229         assertEquals(expected.width(), bounds.width(), 0.0f);
230         assertEquals(expected.height(), bounds.height(), 0.0f);
231         path.computeBounds(bounds, false);
232         assertEquals(expected.width(), bounds.width(), 0.0f);
233         assertEquals(expected.height(), bounds.height(), 0.0f);
234     }
235 
236     @Test
testDeprecatedComputeBounds2()237     public void testDeprecatedComputeBounds2() {
238         RectF expected = new RectF(LEFT, TOP, RIGHT, BOTTOM);
239         Path path = new Path();
240         assertTrue(path.isEmpty());
241         RectF bounds = new RectF(LEFT, TOP, RIGHT, BOTTOM);
242         path.addRect(bounds, Path.Direction.CW);
243         path.computeBounds(bounds, true);
244         assertEquals(expected.width(), bounds.width(), 0.0f);
245         assertEquals(expected.height(), bounds.height(), 0.0f);
246         path.computeBounds(bounds, false);
247         assertEquals(expected.width(), bounds.width(), 0.0f);
248         assertEquals(expected.height(), bounds.height(), 0.0f);
249     }
250 
251     @Test
testComputeBounds1()252     public void testComputeBounds1() {
253         RectF expected = new RectF(0.0f, 0.0f, 0.0f, 0.0f);
254         Path path = new Path();
255         assertTrue(path.isEmpty());
256         RectF bounds = new RectF();
257         path.computeBounds(bounds);
258         assertEquals(expected.width(), bounds.width(), 0.0f);
259         assertEquals(expected.height(), bounds.height(), 0.0f);
260         path.computeBounds(bounds);
261         assertEquals(expected.width(), bounds.width(), 0.0f);
262         assertEquals(expected.height(), bounds.height(), 0.0f);
263     }
264 
265     @Test
testComputeBounds2()266     public void testComputeBounds2() {
267         RectF expected = new RectF(LEFT, TOP, RIGHT, BOTTOM);
268         Path path = new Path();
269         assertTrue(path.isEmpty());
270         RectF bounds = new RectF(LEFT, TOP, RIGHT, BOTTOM);
271         path.addRect(bounds, Path.Direction.CW);
272         path.computeBounds(bounds);
273         assertEquals(expected.width(), bounds.width(), 0.0f);
274         assertEquals(expected.height(), bounds.height(), 0.0f);
275         path.computeBounds(bounds);
276         assertEquals(expected.width(), bounds.width(), 0.0f);
277         assertEquals(expected.height(), bounds.height(), 0.0f);
278     }
279 
280     @Test
testSetLastPoint()281     public void testSetLastPoint() {
282         Path path = new Path();
283         path.setLastPoint(10.0f, 10.0f);
284     }
285 
286     @Test
testRLineTo()287     public void testRLineTo() {
288         Path path = new Path();
289         assertTrue(path.isEmpty());
290         path.rLineTo(10.0f, 10.0f);
291         assertFalse(path.isEmpty());
292     }
293 
294     @Test
testIsEmpty()295     public void testIsEmpty() {
296 
297         Path path = new Path();
298         assertTrue(path.isEmpty());
299         addRectToPath(path);
300         assertFalse(path.isEmpty());
301     }
302 
303     @Test
testRewind()304     public void testRewind() {
305         Path.FillType expected = Path.FillType.EVEN_ODD;
306 
307         Path path = new Path();
308         assertTrue(path.isEmpty());
309         addRectToPath(path);
310         path.rewind();
311         path.setFillType(Path.FillType.EVEN_ODD);
312         assertTrue(path.isEmpty());
313         assertEquals(expected, path.getFillType());
314     }
315 
316     @Test
testAddOval()317     public void testAddOval() {
318         Path path = new Path();
319         assertTrue(path.isEmpty());
320         RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
321         path.addOval(oval, Path.Direction.CW);
322         assertFalse(path.isEmpty());
323     }
324 
325     @Test
testIsRect()326     public void testIsRect() {
327         Path path = new Path();
328         assertTrue(path.isEmpty());
329         addRectToPath(path);
330     }
331 
332     @Test
testAddPath1()333     public void testAddPath1() {
334         Path path = new Path();
335         assertTrue(path.isEmpty());
336         Path src = new Path();
337         addRectToPath(src);
338         path.addPath(src, 10.0f, 10.0f);
339         assertFalse(path.isEmpty());
340     }
341 
342     @Test
testAddPath2()343     public void testAddPath2() {
344         Path path = new Path();
345         assertTrue(path.isEmpty());
346         Path src = new Path();
347         addRectToPath(src);
348         path.addPath(src);
349         assertFalse(path.isEmpty());
350     }
351 
352     @Test
testAddPath3()353     public void testAddPath3() {
354         Path path = new Path();
355         assertTrue(path.isEmpty());
356         Path src = new Path();
357         addRectToPath(src);
358         Matrix matrix = new Matrix();
359         path.addPath(src, matrix);
360         assertFalse(path.isEmpty());
361     }
362 
363     @Test
testAddRoundRect1()364     public void testAddRoundRect1() {
365         Path path = new Path();
366         assertTrue(path.isEmpty());
367         RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
368         path.addRoundRect(rect, XCOORD, YCOORD, Path.Direction.CW);
369         assertFalse(path.isEmpty());
370     }
371 
372     @Test
testAddRoundRect2()373     public void testAddRoundRect2() {
374         Path path = new Path();
375         assertTrue(path.isEmpty());
376         RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
377         float[] radii = new float[8];
378         for (int i = 0; i < 8; i++) {
379             radii[i] = 10.0f + i * 5.0f;
380         }
381         path.addRoundRect(rect, radii, Path.Direction.CW);
382         assertFalse(path.isEmpty());
383     }
384 
385     @Test
testIsConvex1()386     public void testIsConvex1() {
387         Path path = new Path();
388         path.addRect(0, 0, 100, 10, Path.Direction.CW);
389         assertTrue(path.isConvex());
390 
391         path.addRect(0, 0, 10, 100, Path.Direction.CW);
392         assertFalse(path.isConvex()); // path is concave
393     }
394 
395     @Test
testIsConvex2()396     public void testIsConvex2() {
397         Path path = new Path();
398         path.addRect(0, 0, 40, 40, Path.Direction.CW);
399         assertTrue(path.isConvex());
400 
401         path.addRect(10, 10, 30, 30, Path.Direction.CCW);
402         assertFalse(path.isConvex()); // path has hole, isn't convex
403     }
404 
405     @Test
testIsConvex3()406     public void testIsConvex3() {
407         Path path = new Path();
408         path.addRect(0, 0, 10, 10, Path.Direction.CW);
409         assertTrue(path.isConvex());
410 
411         path.addRect(0, 20, 10, 10, Path.Direction.CW);
412         assertFalse(path.isConvex()); // path isn't one convex shape
413     }
414 
415     @Test
testIsInverseFillType()416     public void testIsInverseFillType() {
417         Path path = new Path();
418         assertFalse(path.isInverseFillType());
419         path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
420         assertTrue(path.isInverseFillType());
421     }
422 
423     @Test
testOffset1()424     public void testOffset1() {
425         Path path = new Path();
426         assertTrue(path.isEmpty());
427         addRectToPath(path);
428         Path dst = new Path();
429         path.offset(XCOORD, YCOORD, dst);
430         assertFalse(dst.isEmpty());
431     }
432 
433     @Test
testCubicTo()434     public void testCubicTo() {
435         Path path = new Path();
436         assertTrue(path.isEmpty());
437         path.cubicTo(10.0f, 10.0f, 20.0f, 20.0f, 30.0f, 30.0f);
438         assertFalse(path.isEmpty());
439     }
440 
441     @Test
442     @ApiTest(apis = {"android.graphics.Path#getPathIterator", "android.graphics.PathIterator#next",
443             "android.graphics.PathIterator.Segment#getVerb",
444             "android.graphics.PathIterator.Segment#getPoints",
445             "android.graphics.PathIterator.Segment#getConicWeight",
446             "android.graphics.Path#isEmpty", "android.graphics.Path#conicTo"})
447     @DisabledOnRavenwood(blockedBy = {PathIterator.class})
testConicTo()448     public void testConicTo() {
449         Path path = new Path();
450         assertTrue(path.isEmpty());
451         path.conicTo(10.0f, 10.0f, 20.0f, 20.0f, 2f);
452         assertFalse(path.isEmpty());
453         int verbIndex = 0;
454         for (PathIterator it = path.getPathIterator(); it.hasNext(); ) {
455             PathIterator.Segment segment = it.next();
456             int verb = segment.getVerb();
457             float[] points = segment.getPoints();
458             float weight = segment.getConicWeight();
459             switch (verb) {
460                 case PathIterator.VERB_CONIC:
461                     assertEquals(0f, points[0], 0f);
462                     assertEquals(0f, points[1], 0f);
463                     assertEquals(10f, points[2], 0f);
464                     assertEquals(10f, points[3], 0f);
465                     assertEquals(20f, points[4], 0f);
466                     assertEquals(20f, points[5], 0f);
467                     assertEquals(2f, weight, 0f);
468                     break;
469                 default:
470                     break;
471             }
472         }
473     }
474 
475     @Test
testReset()476     public void testReset() {
477         Path path = new Path();
478         assertTrue(path.isEmpty());
479         Path path1 = new Path();
480         addRectToPath(path1);
481         path.set(path1);
482         assertFalse(path.isEmpty());
483         path.reset();
484         assertTrue(path.isEmpty());
485     }
486 
487     @Test
testResetPreservesFillType()488     public void testResetPreservesFillType() {
489         Path path = new Path();
490 
491         final Path.FillType defaultFillType = path.getFillType();
492         final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
493 
494         // This test is only meaningful if it changes from the default.
495         assertFalse(fillType.equals(defaultFillType));
496 
497         path.setFillType(fillType);
498         path.reset();
499         assertEquals(path.getFillType(), fillType);
500     }
501 
502     @Test
testToggleInverseFillType()503     public void testToggleInverseFillType() {
504         Path path = new Path();
505         assertTrue(path.isEmpty());
506         path.toggleInverseFillType();
507         assertTrue(path.isInverseFillType());
508     }
509 
510     @Test
testAddArc()511     public void testAddArc() {
512         Path path = new Path();
513         assertTrue(path.isEmpty());
514         RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
515         path.addArc(oval, 0.0f, 30.0f);
516         assertFalse(path.isEmpty());
517     }
518 
519     @Test
testRCubicTo()520     public void testRCubicTo() {
521         Path path = new Path();
522         assertTrue(path.isEmpty());
523         path.rCubicTo(10.0f, 10.0f, 11.0f, 11.0f, 12.0f, 12.0f);
524         assertFalse(path.isEmpty());
525     }
526 
527     @Test
528     @ApiTest(apis = {"android.graphics.Path#getPathIterator", "android.graphics.PathIterator#next",
529             "android.graphics.PathIterator.Segment#getVerb",
530             "android.graphics.PathIterator.Segment#getPoints",
531             "android.graphics.PathIterator.Segment#getConicWeight",
532             "android.graphics.Path#isEmpty", "android.graphics.Path#rConicTo"})
533     @DisabledOnRavenwood(blockedBy = {PathIterator.class})
testRConicTo()534     public void testRConicTo() {
535         Path path = new Path();
536         assertTrue(path.isEmpty());
537         path.moveTo(5f, 15f);
538         path.rConicTo(10.0f, 10.0f, 20.0f, 20.0f, 2f);
539         assertFalse(path.isEmpty());
540         int verbIndex = 0;
541         for (PathIterator it = path.getPathIterator(); it.hasNext(); ) {
542             PathIterator.Segment segment = it.next();
543             int verb = segment.getVerb();
544             float[] points = segment.getPoints();
545             float weight = segment.getConicWeight();
546             switch (verb) {
547                 case PathIterator.VERB_MOVE:
548                     assertEquals(5f, points[0], 0f);
549                     assertEquals(15f, points[1], 0f);
550                     break;
551                 case PathIterator.VERB_CONIC:
552                     assertEquals(5f, points[0], 0f);
553                     assertEquals(15f, points[1], 0f);
554                     assertEquals(15f, points[2], 0f);
555                     assertEquals(25f, points[3], 0f);
556                     assertEquals(25f, points[4], 0f);
557                     assertEquals(35f, points[5], 0f);
558                     assertEquals(2f, weight, 0f);
559                     break;
560                 default:
561                     break;
562             }
563         }
564     }
565 
566     @Test
567     @DisabledOnRavenwood(blockedBy = {Paint.class})
testOffsetTextPath()568     public void testOffsetTextPath() {
569         Paint paint = new Paint();
570         Path path = new Path();
571         paint.setTextSize(20);
572         String text = "abc";
573         paint.getTextPath(text, 0, text.length() - 1, 0, 0, path);
574         RectF expectedRect = new RectF();
575         path.computeBounds(expectedRect);
576         assertFalse(expectedRect.isEmpty());
577         int offset = 10;
578         expectedRect.offset(offset, offset);
579 
580         path.offset(offset, offset);
581         RectF offsettedRect = new RectF();
582         path.computeBounds(offsettedRect);
583         assertEquals(expectedRect, offsettedRect);
584     }
585 
586     @Test(expected=IllegalArgumentException.class)
testApproximate_lowError()587     public void testApproximate_lowError() {
588         new Path().approximate(-0.1f);
589     }
590 
591     @Test
testApproximate_rect_cw()592     public void testApproximate_rect_cw() {
593         Path path = new Path();
594         path.addRect(0, 0, 100, 100, Path.Direction.CW);
595         assertArrayEquals(new float[] {
596                 0, 0, 0,
597                 0.25f, 100, 0,
598                 0.50f, 100, 100,
599                 0.75f, 0, 100,
600                 1, 0, 0,
601         }, path.approximate(1f), 0);
602     }
603 
604     @Test
testApproximate_rect_ccw()605     public void testApproximate_rect_ccw() {
606         Path path = new Path();
607         path.addRect(0, 0, 100, 100, Path.Direction.CCW);
608         assertArrayEquals(new float[] {
609                 0, 0, 0,
610                 0.25f, 0, 100,
611                 0.50f, 100, 100,
612                 0.75f, 100, 0,
613                 1, 0, 0,
614         }, path.approximate(1f), 0);
615     }
616 
617     @Test
testApproximate_empty()618     public void testApproximate_empty() {
619         Path path = new Path();
620         assertArrayEquals(new float[] {
621                 0, 0, 0,
622                 1, 0, 0,
623         }, path.approximate(0.5f), 0);
624     }
625 
626     @Test
testApproximate_circle()627     public void testApproximate_circle() {
628         Path path = new Path();
629         path.addCircle(0, 0, 50, Path.Direction.CW);
630         assertTrue(path.approximate(0.25f).length > 20);
631     }
632 
633     @Test
634     @DisabledOnRavenwood(blockedBy = {Paint.class})
testPathOffset()635     public void testPathOffset() {
636         Path actualPath = new Path();
637         actualPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
638         actualPath.offset(OFFSET_X, OFFSET_Y);
639 
640         Path expectedPath = new Path();
641         expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
642                 START_Y + OFFSET_Y + SQUARE, Direction.CW);
643 
644         verifyPathsAreEquivalent(actualPath, expectedPath);
645     }
646 
647     @Test
648     @DisabledOnRavenwood(blockedBy = {Paint.class})
testPathOffset2()649     public void testPathOffset2() {
650         Path actualPath = new Path();
651         actualPath.moveTo(0, 0);
652         actualPath.offset(OFFSET_X, OFFSET_Y);
653         actualPath.lineTo(OFFSET_X + 20, OFFSET_Y);
654 
655 
656         Path expectedPath = new Path();
657         expectedPath.moveTo(OFFSET_X, OFFSET_Y);
658         expectedPath.lineTo(OFFSET_X + 20, OFFSET_Y);
659 
660         Paint paint = new Paint();
661         paint.setColor(Color.RED);
662         paint.setStyle(Paint.Style.STROKE);
663         verifyPathsAreEquivalent(actualPath, expectedPath, paint);
664     }
665 
666     @Test
667     @DisabledOnRavenwood(blockedBy = {Paint.class})
testPathOffsetWithDestination()668     public void testPathOffsetWithDestination() {
669         Path initialPath = new Path();
670         initialPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
671         Path actualPath = new Path();
672         initialPath.offset(OFFSET_X, OFFSET_Y, actualPath);
673 
674         Path expectedPath = new Path();
675         expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
676                 START_Y + OFFSET_Y + SQUARE, Direction.CW);
677 
678         verifyPathsAreEquivalent(actualPath, expectedPath);
679     }
680 
681     /** This test just ensures the process doesn't crash. The actual output is not interesting
682      *  hence the lack of asserts, as the only behavior that's being asserted is that it
683      *  doesn't crash.
684      */
685     @Test
testUseAfterFinalize()686     public void testUseAfterFinalize() throws Throwable {
687         PathAbuser pathAbuser = new PathAbuser();
688 
689         // Basic test that we created a path successfully
690         assertTrue(pathAbuser.isEmpty());
691         addRectToPath(pathAbuser);
692         assertTrue(pathAbuser.isRect(null));
693         assertFalse(pathAbuser.isEmpty());
694 
695         // Now use-after-finalize.
696         pathAbuser.destroy();
697         pathAbuser.isEmpty();
698         pathAbuser.isRect(null);
699         pathAbuser.destroy();
700         pathAbuser.isEmpty();
701         pathAbuser.isRect(null);
702         pathAbuser.destroy();
703     }
704 
705     @Test
706     @ApiTest(apis = {"android.graphics.Path#moveTo", "android.graphics.Path#lineTo",
707             "android.graphics.Path#quadTo", "android.graphics.Path#conicTo",
708             "android.graphics.Path#cubicTo", "android.graphics.Path#close",
709             "android.graphics.Path#getGenerationId",
710     })
testGenerationId()711     public void testGenerationId() {
712         Path path = new Path();
713         path.moveTo(1f, 2f);
714         int generationId = path.getGenerationId();
715 
716         path.lineTo(3f, 4f);
717         assertNotEquals(generationId, path.getGenerationId());
718         generationId = path.getGenerationId();
719 
720         path.moveTo(5f, 6f);
721         assertNotEquals(generationId, path.getGenerationId());
722         generationId = path.getGenerationId();
723 
724         path.quadTo(7f, 8f, 9f, 10f);
725         assertNotEquals(generationId, path.getGenerationId());
726         generationId = path.getGenerationId();
727 
728         path.conicTo(11f, 12f, 13f, 14f, 2f);
729         assertNotEquals(generationId, path.getGenerationId());
730         generationId = path.getGenerationId();
731 
732         path.cubicTo(15f, 16f, 17f, 18f, 19f, 20f);
733         assertNotEquals(generationId, path.getGenerationId());
734         generationId = path.getGenerationId();
735 
736         path.close();
737         assertNotEquals(generationId, path.getGenerationId());
738     }
739 
740     @Test
741     @ApiTest(apis = {"android.graphics.Path#moveTo", "android.graphics.Path#lineTo",
742             "android.graphics.PathIterator#next",
743             "android.graphics.Path#isInterpolatable",
744             "android.graphics.Path#interpolate",
745     })
746     @DisabledOnRavenwood(blockedBy = {Paint.class})
testPathInterpolation()747     public void testPathInterpolation() {
748         Path startPath = new Path();
749         Path endPath = new Path();
750         startPath.moveTo(100f, 100f);
751         startPath.lineTo(200f, 300f);
752         endPath.moveTo(200f, 200f);
753         endPath.lineTo(600f, 700f);
754 
755         Path interpolatedPath = new Path();
756         assertTrue(startPath.isInterpolatable(endPath));
757 
758         startPath.interpolate(endPath, .5f, interpolatedPath);
759         PathIterator iterator = interpolatedPath.getPathIterator();
760         float[] points = new float[8];
761         int verb = iterator.next(points, 0);
762         assertEquals(PathIterator.VERB_MOVE, verb);
763         assertEquals(150f, points[0], .001f);
764         assertEquals(150f, points[1], .001f);
765         verb = iterator.next(points, 0);
766         assertEquals(PathIterator.VERB_LINE, verb);
767         assertEquals(400f, points[2], .001f);
768         assertEquals(500f, points[3], .001f);
769     }
770 
assertPointsEqual(float[] points1, float[] points2, int numToCheck)771     private void assertPointsEqual(float[] points1, float[] points2, int numToCheck) {
772         for (int i = 0; i < numToCheck; ++i) {
773             assertEquals("point " + i + "not equal", points1[i], points2[i], 0f);
774         }
775     }
776 
777     private static final class PathAbuser extends Path {
destroy()778         public void destroy() throws Throwable {
779             finalize();
780         }
781     }
782 
verifyPathsAreEquivalent(Path actual, Path expected)783     private static void verifyPathsAreEquivalent(Path actual, Path expected) {
784         Paint paint = new Paint();
785         paint.setColor(Color.RED);
786         verifyPathsAreEquivalent(actual, expected, paint);
787     }
788 
verifyPathsAreEquivalent(Path actual, Path expected, Paint paint)789     private static void verifyPathsAreEquivalent(Path actual, Path expected, Paint paint) {
790         Bitmap actualBitmap = drawAndGetBitmap(actual, paint);
791         Bitmap expectedBitmap = drawAndGetBitmap(expected, paint);
792         assertTrue(actualBitmap.sameAs(expectedBitmap));
793     }
794 
drawAndGetBitmap(Path path, Paint paint)795     private static Bitmap drawAndGetBitmap(Path path, Paint paint) {
796         Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
797         bitmap.eraseColor(Color.BLACK);
798         Canvas canvas = new Canvas(bitmap);
799         canvas.drawPath(path, paint);
800         return bitmap;
801     }
802 
addRectToPath(Path path)803     private static void addRectToPath(Path path) {
804         RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
805         path.addRect(rect, Path.Direction.CW);
806     }
807 }
808