Add tests for MotionEvent transforms.

Change-Id: I1d8ed0840a17ec21dcd9d57fb9c5320688448789
diff --git a/tests/tests/view/src/android/view/cts/MotionEventTest.java b/tests/tests/view/src/android/view/cts/MotionEventTest.java
index e4df594..f8a14e6 100644
--- a/tests/tests/view/src/android/view/cts/MotionEventTest.java
+++ b/tests/tests/view/src/android/view/cts/MotionEventTest.java
@@ -22,12 +22,15 @@
 import dalvik.annotation.TestTargets;
 import dalvik.annotation.ToBeFixed;
 
+import android.graphics.Matrix;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
+import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
 
 /**
  * Test {@link MotionEvent}.
@@ -497,4 +500,103 @@
         
         mMotionEvent2 = null; // since it was recycled, don't try to recycle again in tear down
     }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "transform",
+            args = {}
+        )
+    public void testTransformShouldThrowWhenMatrixIsNull() {
+        try {
+            mMotionEvent1.transform(null);
+            fail("transform() should throw an exception when matrix is null.");
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "transform",
+            args = {}
+        )
+    public void testTransformShouldApplyMatrixToPointsAndPreserveRawPosition() {
+        // Generate some points on a circle.
+        // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle
+        // of ARC * i degrees clockwise relative to the Y axis.
+        // The geometrical representation is irrelevant to the test, it's just easy to generate
+        // and check rotation.  We set the orientation to the same angle.
+        // Coordinate system: down is increasing Y, right is increasing X.
+        final float PI_180 = (float) (Math.PI / 180);
+        final float RADIUS = 10;
+        final float ARC = 36;
+        final float ROTATION = ARC * 2;
+
+        final int pointerCount = 11;
+        final int[] pointerIds = new int[pointerCount];
+        final PointerCoords[] pointerCoords = new PointerCoords[pointerCount];
+        for (int i = 0; i < pointerCount; i++) {
+            final PointerCoords c = new PointerCoords();
+            final float angle = (float) (i * ARC * PI_180);
+            pointerIds[i] = i;
+            pointerCoords[i] = c;
+            c.x = (float) (Math.sin(angle) * RADIUS + 3);
+            c.y = (float) (- Math.cos(angle) * RADIUS + 2);
+            c.orientation = angle;
+        }
+        final MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+                pointerCount, pointerIds, pointerCoords, 0, 0, 0, 0, 0, 0, 0);
+        final float originalRawX = 0 + 3;
+        final float originalRawY = - RADIUS + 2;
+        dump("Original points.", event);
+
+        // Check original raw X and Y assumption.
+        assertEquals(originalRawX, event.getRawX(), 0.001);
+        assertEquals(originalRawY, event.getRawY(), 0.001);
+
+        // Now translate the motion event so the circle's origin is at (0,0).
+        event.offsetLocation(-3, -2);
+        dump("Translated points.", event);
+
+        // Offsetting the location should preserve the raw X and Y of the first point.
+        assertEquals(originalRawX, event.getRawX(), 0.001);
+        assertEquals(originalRawY, event.getRawY(), 0.001);
+
+        // Apply a rotation about the origin by ROTATION degrees clockwise.
+        Matrix matrix = new Matrix();
+        matrix.setRotate(ROTATION);
+        event.transform(matrix);
+        dump("Rotated points.", event);
+
+        // Check the points.
+        for (int i = 0; i < pointerCount; i++) {
+            final PointerCoords c = pointerCoords[i];
+            event.getPointerCoords(i, c);
+
+            final float angle = (float) ((i * ARC + ROTATION) * PI_180);
+            assertEquals(Math.sin(angle) * RADIUS, c.x, 0.001);
+            assertEquals(- Math.cos(angle) * RADIUS, c.y, 0.001);
+            assertEquals(Math.tan(angle), Math.tan(c.orientation), 0.1);
+        }
+
+        // Applying the transformation should preserve the raw X and Y of the first point.
+        assertEquals(originalRawX, event.getRawX(), 0.001);
+        assertEquals(originalRawY, event.getRawY(), 0.001);
+    }
+
+    private void dump(String label, MotionEvent ev) {
+        if (false) {
+            StringBuilder msg = new StringBuilder();
+            msg.append(label).append("\n");
+
+            msg.append("  Raw: (").append(ev.getRawX()).append(",").append(ev.getRawY()).append(")\n");
+            int pointerCount = ev.getPointerCount();
+            for (int i = 0; i < pointerCount; i++) {
+                msg.append("  Pointer[").append(i).append("]: (")
+                        .append(ev.getX(i)).append(",").append(ev.getY(i)).append("), orientation=")
+                        .append(ev.getOrientation(i) * 180 / Math.PI).append(" deg\n");
+            }
+
+            android.util.Log.i("TEST", msg.toString());
+        }
+    }
 }