changeset 10907:248052231a3a

8062497: Open up some AffineTransform tests Reviewed-by: jgodinez
author prr
date Thu, 30 Oct 2014 11:08:27 -0700
parents c6b695f708f1
children 4f6bff961a31
files test/java/awt/geom/AffineTransform/GetTypeOptimization.java test/java/awt/geom/AffineTransform/TestInvertMethods.java test/java/awt/geom/AffineTransform/TestRotateMethods.java test/java/awt/geom/AffineTransform/TestSerialization.java test/java/awt/geom/AffineTransform/serial.1.2
diffstat 5 files changed, 1301 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/geom/AffineTransform/GetTypeOptimization.java	Thu Oct 30 11:08:27 2014 -0700
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 4418285
+ * @summary Tests that transforms modified with degenerate operations
+ *          continue to return their more optimal type from getType().
+ *          This test also confirms that isIdentity() returns the
+ *          optimal value under all histories of modification.
+ * @run main GetTypeOptimization
+ */
+
+import java.awt.geom.AffineTransform;
+import java.util.Random;
+
+public class GetTypeOptimization {
+    static int TYPE_IDENTITY          = AffineTransform.TYPE_IDENTITY;
+    static int TYPE_TRANSLATION       = AffineTransform.TYPE_TRANSLATION;
+    static int TYPE_UNIFORM_SCALE     = AffineTransform.TYPE_UNIFORM_SCALE;
+    static int TYPE_GENERAL_SCALE     = AffineTransform.TYPE_GENERAL_SCALE;
+    static int TYPE_FLIP              = AffineTransform.TYPE_FLIP;
+    static int TYPE_QUADRANT_ROTATION = AffineTransform.TYPE_QUADRANT_ROTATION;
+    static int TYPE_GENERAL_ROTATION  = AffineTransform.TYPE_GENERAL_ROTATION;
+    static int TYPE_GENERAL_TRANSFORM = AffineTransform.TYPE_GENERAL_TRANSFORM;
+
+    public static Random rand = new Random();
+
+    public static boolean verbose;
+    public static int numerrors;
+
+    public static void main(String argv[]) {
+        verbose = (argv.length != 0);
+
+        checkBug4418285();
+
+        checkAtType(new AffineTransform());
+        checkAtType(AffineTransform.getTranslateInstance(0, 0));
+        checkAtType(AffineTransform.getScaleInstance(1, 1));
+        checkAtType(AffineTransform.getShearInstance(0, 0));
+        checkAtType(AffineTransform.getRotateInstance(0));
+        checkAtType(AffineTransform.getRotateInstance(0, 0, 0));
+        for (int i = 90; i <= 360; i += 90) {
+            double angle = Math.toRadians(i);
+            checkAtType(AffineTransform.getRotateInstance(angle));
+            checkAtType(AffineTransform.getRotateInstance(angle, 0, 0));
+        }
+
+        AffineTransform at = new AffineTransform();
+        checkAtType(at);
+
+        at.setToIdentity(); checkAtType(at);
+        at.setToTranslation(0.0, 0.0); checkAtType(at);
+        at.setToScale(1.0, 1.0); checkAtType(at);
+        at.setToShear(0.0, 0.0); checkAtType(at);
+        at.setToRotation(0); checkAtType(at);
+        at.setToRotation(0, 0, 0); checkAtType(at);
+        for (int i = 90; i <= 360; i += 90) {
+            double angle = Math.toRadians(i);
+            at.setToRotation(angle); checkAtType(at);
+            at.setToRotation(angle, 0, 0); checkAtType(at);
+        }
+
+        at.setToIdentity(); at.scale(1, 1); checkAtType(at);
+        at.setToIdentity(); at.translate(0, 0); checkAtType(at);
+        at.setToIdentity(); at.shear(0, 0); checkAtType(at);
+        at.setToIdentity(); at.rotate(0); checkAtType(at);
+        for (int i = 90; i <= 360; i += 90) {
+            double angle = Math.toRadians(i);
+            at.setToIdentity(); at.rotate(angle); checkAtType(at);
+            at.setToIdentity(); at.rotate(angle, 0, 0); checkAtType(at);
+        }
+
+        at.setToIdentity();
+        for (int i = 0; i < 4; i++) {
+            at.rotate(Math.toRadians(90)); checkAtType(at);
+        }
+
+        at.setToIdentity();
+        at.scale(2, 2); checkAtType(at);
+        at.scale(.5, .5); checkAtType(at);
+
+        for (int n = 1; n <= 3; n++) {
+            for (int i = 0; i < 500; i++) {
+                checkAtType(makeRandomTransform(n));
+            }
+        }
+        if (numerrors != 0) {
+            if (!verbose) {
+                System.err.println("Rerun test with an argument for details");
+            }
+            throw new RuntimeException(numerrors+" tests failed!");
+        }
+    }
+
+    public static void checkBug4418285() {
+        AffineTransform id =
+            new AffineTransform ();
+        AffineTransform translate0 =
+            AffineTransform.getTranslateInstance (0, 0);
+        if (id.isIdentity() != translate0.isIdentity() ||
+            id.getType() != translate0.getType())
+        {
+            numerrors++;
+            if (verbose) {
+                System.err.println("id=        " + id         +
+                                   ", isIdentity()=" +
+                                   id.isIdentity());
+                System.err.println("translate0=" + translate0 +
+                                   ", isIdentity()=" +
+                                   translate0.isIdentity());
+                System.err.println("equals="     + id.equals (translate0));
+                System.err.println();
+            }
+        }
+    }
+
+    public static AffineTransform makeRandomTransform(int numops) {
+        AffineTransform at = new AffineTransform();
+        while (--numops >= 0) {
+            switch (rand.nextInt(4)) {
+            case 0:
+                at.scale(rand.nextDouble() * 5 - 2.5,
+                         rand.nextDouble() * 5 - 2.5);
+                break;
+            case 1:
+                at.shear(rand.nextDouble() * 5 - 2.5,
+                         rand.nextDouble() * 5 - 2.5);
+                break;
+            case 2:
+                at.rotate(rand.nextDouble() * Math.PI * 2);
+                break;
+            case 3:
+                at.translate(rand.nextDouble() * 50 - 25,
+                             rand.nextDouble() * 50 - 25);
+                break;
+            default:
+                throw new InternalError("bad case!");
+            }
+        }
+        return at;
+    }
+
+    public static void checkAtType(AffineTransform at) {
+        int reftype = getRefType(at);
+        boolean isident = isIdentity(at);
+        for (int i = 0; i < 5; i++) {
+            boolean atisident = at.isIdentity();
+            int attype = at.getType();
+            if (isident != atisident || reftype != attype) {
+                numerrors++;
+                if (verbose) {
+                    System.err.println(at+".isIdentity() == "+atisident);
+                    System.err.println(at+".getType() == "+attype);
+                    System.err.println("should be "+isident+", "+reftype);
+                    new Throwable().printStackTrace();
+                    System.err.println();
+                }
+                break;
+            }
+        }
+    }
+
+    public static boolean isIdentity(AffineTransform at) {
+        return (at.getScaleX() == 1 &&
+                at.getScaleY() == 1 &&
+                at.getShearX() == 0 &&
+                at.getShearY() == 0 &&
+                at.getTranslateX() == 0 &&
+                at.getTranslateY() == 0);
+
+    }
+
+    public static int getRefType(AffineTransform at) {
+        double m00 = at.getScaleX();
+        double m11 = at.getScaleY();
+        double m01 = at.getShearX();
+        double m10 = at.getShearY();
+        if (m00 * m01 + m10 * m11 != 0) {
+            // Transformed unit vectors are not perpendicular...
+            return TYPE_GENERAL_TRANSFORM;
+        }
+        int type = ((at.getTranslateX() != 0 || at.getTranslateY() != 0)
+                    ? TYPE_TRANSLATION : TYPE_IDENTITY);
+        boolean sgn0, sgn1;
+        if (m01 == 0 && m10 == 0) {
+            sgn0 = (m00 >= 0.0);
+            sgn1 = (m11 >= 0.0);
+            if (sgn0 == sgn1) {
+                if (sgn0) {
+                    // Both scaling factors non-negative - simple scale
+                    if (m00 != m11) {
+                        type |= TYPE_GENERAL_SCALE;
+                    } else if (m00 != 1.0) {
+                        type |= TYPE_UNIFORM_SCALE;
+                    }
+                } else {
+                    // Both scaling factors negative - 180 degree rotation
+                    type |= TYPE_QUADRANT_ROTATION;
+                    if (m00 != m11) {
+                        type |= TYPE_GENERAL_SCALE;
+                    } else if (m00 != -1.0) {
+                        type |= TYPE_UNIFORM_SCALE;
+                    }
+                }
+            } else {
+                // Scaling factor signs different - flip about some axis
+                type |= TYPE_FLIP;
+                if (m00 != -m11) {
+                    type |= TYPE_GENERAL_SCALE;
+                } else if (m00 != 1.0 && m00 != -1.0) {
+                    type |= TYPE_UNIFORM_SCALE;
+                }
+            }
+        } else if (m00 == 0 && m11 == 0) {
+            sgn0 = (m01 >= 0.0);
+            sgn1 = (m10 >= 0.0);
+            if (sgn0 != sgn1) {
+                // Different signs - simple 90 degree rotation
+                if (m01 != -m10) {
+                    type |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
+                } else if (m01 != 1.0 && m01 != -1.0) {
+                    type |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
+                } else {
+                    type |= TYPE_QUADRANT_ROTATION;
+                }
+            } else {
+                // Same signs - 90 degree rotation plus an axis flip too
+                if (m01 == m10) {
+                    if (m01 == 0) {
+                        // All four m[01][01] elements are 0
+                        type |= TYPE_UNIFORM_SCALE;
+                    } else {
+                        // Note - shouldn't (1,1) be no scale at all?
+                        type |= (TYPE_QUADRANT_ROTATION |
+                                 TYPE_FLIP |
+                                 TYPE_UNIFORM_SCALE);
+                    }
+                } else {
+                    type |= (TYPE_QUADRANT_ROTATION |
+                             TYPE_FLIP |
+                             TYPE_GENERAL_SCALE);
+                }
+            }
+        } else {
+            if (m00 * m11 >= 0.0) {
+                // sgn(m00) == sgn(m11) therefore sgn(m01) == -sgn(m10)
+                // This is the "unflipped" (right-handed) state
+                if (m00 != m11 || m01 != -m10) {
+                    type |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
+                } else if (m00 == 0) {
+                    // then m11 == 0 also
+                    if (m01 == -m10) {
+                        type |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
+                    } else {
+                        type |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
+                    }
+                } else if (m00 * m11 - m01 * m10 != 1.0) {
+                    type |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
+                } else {
+                    type |= TYPE_GENERAL_ROTATION;
+                }
+            } else {
+                // sgn(m00) == -sgn(m11) therefore sgn(m01) == sgn(m10)
+                // This is the "flipped" (left-handed) state
+                if (m00 != -m11 || m01 != m10) {
+                    type |= (TYPE_GENERAL_ROTATION |
+                             TYPE_FLIP |
+                             TYPE_GENERAL_SCALE);
+                } else if (m01 == 0) {
+                    if (m00 == 1.0 || m00 == -1.0) {
+                        type |= TYPE_FLIP;
+                    } else {
+                        type |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
+                    }
+                } else if (m00 * m11 - m01 * m10 != 1.0) {
+                    type |= (TYPE_GENERAL_ROTATION |
+                             TYPE_FLIP |
+                             TYPE_UNIFORM_SCALE);
+                } else {
+                    type |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
+                }
+            }
+        }
+        return type;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/geom/AffineTransform/TestInvertMethods.java	Thu Oct 30 11:08:27 2014 -0700
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 4987374
+ * @summary Unit test for inversion methods:
+ *
+ *          AffineTransform.createInverse();
+ *          AffineTransform.invert();
+ *
+ * @author flar
+ * @run main TestInvertMethods
+ */
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+
+/*
+ * Instances of the inner class Tester are "nodes" which take an input
+ * AffineTransform (AT), modify it in some way and pass the modified
+ * AT onto another Tester node.
+ *
+ * There is one particular Tester node of note called theVerifier.
+ * This is a leaf node which takes the input AT and tests the various
+ * inversion methods on that matrix.
+ *
+ * Most of the other Tester nodes will perform a single affine operation
+ * on their input, such as a rotate by various angles, or a scale by
+ * various predefined scale  values, and then pass the modified AT on
+ * to the next node in the chain which may be a verifier or another
+ * modifier.
+ *
+ * The Tester instances can also be chained together using the chain
+ * method so that we can test not only matrices modified by some single
+ * affine operation (scale, rotate, etc.) but also composite matrices
+ * that represent multiple operations concatenated together.
+ */
+public class TestInvertMethods {
+    public static boolean verbose;
+
+    public static final double MAX_ULPS = 2.0;
+    public static double MAX_TX_ULPS = MAX_ULPS;
+    public static double maxulps = 0.0;
+    public static double maxtxulps = 0.0;
+    public static int numtests = 0;
+
+    public static void main(String argv[]) {
+        Tester rotate = new Tester.Rotate();
+        Tester scale = new Tester.Scale();
+        Tester shear = new Tester.Shear();
+        Tester translate = new Tester.Translate();
+
+        if (argv.length > 1) {
+            // This next line verifies that chaining works correctly...
+            scale.chain(translate.chain(new Tester.Debug())).test(false);
+            return;
+        }
+
+        verbose = (argv.length > 0);
+
+        new Tester.Identity().test(true);
+        translate.test(true);
+        scale.test(true);
+        rotate.test(true);
+        shear.test(true);
+        scale.chain(translate).test(true);
+        rotate.chain(translate).test(true);
+        shear.chain(translate).test(true);
+        translate.chain(scale).test(true);
+        translate.chain(rotate).test(true);
+        translate.chain(shear).test(true);
+        translate.chain(scale.chain(rotate.chain(shear))).test(false);
+        shear.chain(rotate.chain(scale.chain(translate))).test(false);
+
+        System.out.println(numtests+" tests performed");
+        System.out.println("Max scale and shear difference: "+maxulps+" ulps");
+        System.out.println("Max translate difference: "+maxtxulps+" ulps");
+    }
+
+    public abstract static class Tester {
+        public static AffineTransform IdentityTx = new AffineTransform();
+
+        /*
+         * This is the leaf node that performs inversion testing
+         * on the incoming AffineTransform.
+         */
+        public static final Tester theVerifier = new Tester() {
+            public void test(AffineTransform at, boolean full) {
+                numtests++;
+                AffineTransform inv1, inv2;
+                boolean isinvertible =
+                    (Math.abs(at.getDeterminant()) >= Double.MIN_VALUE);
+                try {
+                    inv1 = at.createInverse();
+                    if (!isinvertible) missingNTE("createInverse", at);
+                } catch (NoninvertibleTransformException e) {
+                    inv1 = null;
+                    if (isinvertible) extraNTE("createInverse", at);
+                }
+                inv2 = new AffineTransform(at);
+                try {
+                    inv2.invert();
+                    if (!isinvertible) missingNTE("invert", at);
+                } catch (NoninvertibleTransformException e) {
+                    if (isinvertible) extraNTE("invert", at);
+                }
+                if (verbose) System.out.println("at = "+at);
+                if (isinvertible) {
+                    if (verbose) System.out.println(" inv1 = "+inv1);
+                    if (verbose) System.out.println(" inv2 = "+inv2);
+                    if (!inv1.equals(inv2)) {
+                        report(at, inv1, inv2,
+                               "invert methods do not agree");
+                    }
+                    inv1.concatenate(at);
+                    inv2.concatenate(at);
+                    // "Fix" some values that don't always behave
+                    // well with all the math that we've done up
+                    // to this point.
+                    // See the note on the concatfix method below.
+                    concatfix(inv1);
+                    concatfix(inv2);
+                    if (verbose) System.out.println("  at*inv1 = "+inv1);
+                    if (verbose) System.out.println("  at*inv2 = "+inv2);
+                    if (!compare(inv1, IdentityTx)) {
+                        report(at, inv1, IdentityTx,
+                               "createInverse() check failed");
+                    }
+                    if (!compare(inv2, IdentityTx)) {
+                        report(at, inv2, IdentityTx,
+                               "invert() check failed");
+                    }
+                } else {
+                    if (verbose) System.out.println(" is not invertible");
+                }
+                if (verbose) System.out.println();
+            }
+
+            void missingNTE(String methodname, AffineTransform at) {
+                throw new RuntimeException("Noninvertible was not "+
+                                           "thrown from "+methodname+
+                                           " for: "+at);
+            }
+
+            void extraNTE(String methodname, AffineTransform at) {
+                throw new RuntimeException("Unexpected Noninvertible "+
+                                           "thrown from "+methodname+
+                                           " for: "+at);
+            }
+        };
+
+        /*
+         * The inversion math may work out fairly exactly, but when
+         * we concatenate the inversions back with the original matrix
+         * in an attempt to restore them to the identity matrix,
+         * then we can end up compounding errors to a fairly high
+         * level, particularly if the component values had mantissas
+         * that were repeating fractions.  This function therefore
+         * "fixes" the results of concatenating the inversions back
+         * with their original matrices to get rid of small variations
+         * in the values that should have ended up being 0.0.
+         */
+        public void concatfix(AffineTransform at) {
+            double m00 = at.getScaleX();
+            double m10 = at.getShearY();
+            double m01 = at.getShearX();
+            double m11 = at.getScaleY();
+            double m02 = at.getTranslateX();
+            double m12 = at.getTranslateY();
+            if (Math.abs(m02) < 1E-10) m02 = 0.0;
+            if (Math.abs(m12) < 1E-10) m12 = 0.0;
+            if (Math.abs(m01) < 1E-15) m01 = 0.0;
+            if (Math.abs(m10) < 1E-15) m10 = 0.0;
+            at.setTransform(m00, m10,
+                            m01, m11,
+                            m02, m12);
+        }
+
+        public void test(boolean full) {
+            test(IdentityTx, full);
+        }
+
+        public void test(AffineTransform init, boolean full) {
+            test(init, theVerifier, full);
+        }
+
+        public void test(AffineTransform init, Tester next, boolean full) {
+            next.test(init, full);
+        }
+
+        public Tester chain(Tester next) {
+            return new Chain(this, next);
+        }
+
+        /*
+         * Utility node used to chain together two other nodes for
+         * implementing the "chain" method.
+         */
+        public static class Chain extends Tester {
+            Tester prev;
+            Tester next;
+
+            public Chain(Tester prev, Tester next) {
+                this.prev = prev;
+                this.next = next;
+            }
+
+            public void test(AffineTransform init, boolean full) {
+                prev.test(init, next, full);
+            }
+
+            public Tester chain(Tester next) {
+                this.next = this.next.chain(next);
+                return this;
+            }
+        }
+
+        /*
+         * Utility node for testing.
+         */
+        public static class Fail extends Tester {
+            public void test(AffineTransform init, Tester next, boolean full) {
+                throw new RuntimeException("Debug: Forcing failure");
+            }
+        }
+
+        /*
+         * Utility node for testing that chaining works.
+         */
+        public static class Debug extends Tester {
+            public void test(AffineTransform init, Tester next, boolean full) {
+                new Throwable().printStackTrace();
+                next.test(init, full);
+            }
+        }
+
+        /*
+         * NOP node.
+         */
+        public static class Identity extends Tester {
+            public void test(AffineTransform init, Tester next, boolean full) {
+                if (verbose) System.out.println("*Identity = "+init);
+                next.test(init, full);
+            }
+        }
+
+        /*
+         * Affine rotation node.
+         */
+        public static class Rotate extends Tester {
+            public void test(AffineTransform init, Tester next, boolean full) {
+                int inc = full ? 10 : 45;
+                for (int i = -720; i <= 720; i += inc) {
+                    AffineTransform at2 = new AffineTransform(init);
+                    at2.rotate(Math.toRadians(i));
+                    if (verbose) System.out.println("*Rotate("+i+") = "+at2);
+                    next.test(at2, full);
+                }
+            }
+        }
+
+        public static final double SMALL_VALUE = .0001;
+        public static final double LARGE_VALUE = 10000;
+
+        /*
+         * Affine scale node.
+         */
+        public static class Scale extends Tester {
+            public double fullvals[] = {
+                // Noninvertibles
+                0.0, 0.0,
+                0.0, 1.0,
+                1.0, 0.0,
+
+                // Invertibles
+                SMALL_VALUE, SMALL_VALUE,
+                SMALL_VALUE, 1.0,
+                1.0, SMALL_VALUE,
+
+                SMALL_VALUE, LARGE_VALUE,
+                LARGE_VALUE, SMALL_VALUE,
+
+                LARGE_VALUE, LARGE_VALUE,
+                LARGE_VALUE, 1.0,
+                1.0, LARGE_VALUE,
+
+                0.5, 0.5,
+                1.0, 1.0,
+                2.0, 2.0,
+                Math.PI, Math.E,
+            };
+            public double abbrevvals[] = {
+                0.0, 0.0,
+                1.0, 1.0,
+                2.0, 3.0,
+            };
+
+            public void test(AffineTransform init, Tester next, boolean full) {
+                double scales[] = (full ? fullvals : abbrevvals);
+                for (int i = 0; i < scales.length; i += 2) {
+                    AffineTransform at2 = new AffineTransform(init);
+                    at2.scale(scales[i], scales[i+1]);
+                    if (verbose) System.out.println("*Scale("+scales[i]+", "+
+                                                    scales[i+1]+") = "+at2);
+                    next.test(at2, full);
+                }
+            }
+        }
+
+        /*
+         * Affine shear node.
+         */
+        public static class Shear extends Tester {
+            public double fullvals[] = {
+                0.0, 0.0,
+                0.0, 1.0,
+                1.0, 0.0,
+
+                // Noninvertible
+                1.0, 1.0,
+
+                SMALL_VALUE, SMALL_VALUE,
+                SMALL_VALUE, LARGE_VALUE,
+                LARGE_VALUE, SMALL_VALUE,
+                LARGE_VALUE, LARGE_VALUE,
+
+                Math.PI, Math.E,
+            };
+            public double abbrevvals[] = {
+                0.0, 0.0,
+                0.0, 1.0,
+                1.0, 0.0,
+
+                // Noninvertible
+                1.0, 1.0,
+            };
+
+            public void test(AffineTransform init, Tester next, boolean full) {
+                double shears[] = (full ? fullvals : abbrevvals);
+                for (int i = 0; i < shears.length; i += 2) {
+                    AffineTransform at2 = new AffineTransform(init);
+                    at2.shear(shears[i], shears[i+1]);
+                    if (verbose) System.out.println("*Shear("+shears[i]+", "+
+                                                    shears[i+1]+") = "+at2);
+                    next.test(at2, full);
+                }
+            }
+        }
+
+        /*
+         * Affine translate node.
+         */
+        public static class Translate extends Tester {
+            public double fullvals[] = {
+                0.0, 0.0,
+                0.0, 1.0,
+                1.0, 0.0,
+
+                SMALL_VALUE, SMALL_VALUE,
+                SMALL_VALUE, LARGE_VALUE,
+                LARGE_VALUE, SMALL_VALUE,
+                LARGE_VALUE, LARGE_VALUE,
+
+                Math.PI, Math.E,
+            };
+            public double abbrevvals[] = {
+                0.0, 0.0,
+                0.0, 1.0,
+                1.0, 0.0,
+                Math.PI, Math.E,
+            };
+
+            public void test(AffineTransform init, Tester next, boolean full) {
+                double translates[] = (full ? fullvals : abbrevvals);
+                for (int i = 0; i < translates.length; i += 2) {
+                    AffineTransform at2 = new AffineTransform(init);
+                    at2.translate(translates[i], translates[i+1]);
+                    if (verbose) System.out.println("*Translate("+
+                                                    translates[i]+", "+
+                                                    translates[i+1]+") = "+at2);
+                    next.test(at2, full);
+                }
+            }
+        }
+    }
+
+    public static void report(AffineTransform orig,
+                              AffineTransform at1, AffineTransform at2,
+                              String message)
+    {
+        System.out.println(orig+", type = "+orig.getType());
+        System.out.println(at1+", type = "+at1.getType());
+        System.out.println(at2+", type = "+at2.getType());
+        System.out.println("ScaleX values differ by "+
+                           ulps(at1.getScaleX(),
+                                at2.getScaleX())+" ulps");
+        System.out.println("ScaleY values differ by "+
+                           ulps(at1.getScaleY(),
+                                at2.getScaleY())+" ulps");
+        System.out.println("ShearX values differ by "+
+                           ulps(at1.getShearX(),
+                                at2.getShearX())+" ulps");
+        System.out.println("ShearY values differ by "+
+                           ulps(at1.getShearY(),
+                                at2.getShearY())+" ulps");
+        System.out.println("TranslateX values differ by "+
+                           ulps(at1.getTranslateX(),
+                                at2.getTranslateX())+" ulps");
+        System.out.println("TranslateY values differ by "+
+                           ulps(at1.getTranslateY(),
+                                at2.getTranslateY())+" ulps");
+        throw new RuntimeException(message);
+    }
+
+    public static boolean compare(AffineTransform at1, AffineTransform at2) {
+        maxulps = Math.max(maxulps, ulps(at1.getScaleX(), at2.getScaleX()));
+        maxulps = Math.max(maxulps, ulps(at1.getScaleY(), at2.getScaleY()));
+        maxulps = Math.max(maxulps, ulps(at1.getShearX(), at2.getShearX()));
+        maxulps = Math.max(maxulps, ulps(at1.getShearY(), at2.getShearY()));
+        maxtxulps = Math.max(maxtxulps,
+                             ulps(at1.getTranslateX(), at2.getTranslateX()));
+        maxtxulps = Math.max(maxtxulps,
+                             ulps(at1.getTranslateY(), at2.getTranslateY()));
+        return (getModifiedType(at1) == getModifiedType(at2) &&
+                (compare(at1.getScaleX(), at2.getScaleX(), MAX_ULPS)) &&
+                (compare(at1.getScaleY(), at2.getScaleY(), MAX_ULPS)) &&
+                (compare(at1.getShearX(), at2.getShearX(), MAX_ULPS)) &&
+                (compare(at1.getShearY(), at2.getShearY(), MAX_ULPS)) &&
+                (compare(at1.getTranslateX(),
+                         at2.getTranslateX(), MAX_TX_ULPS)) &&
+                (compare(at1.getTranslateY(),
+                         at2.getTranslateY(), MAX_TX_ULPS)));
+    }
+
+    public static final int ANY_SCALE_MASK =
+        (AffineTransform.TYPE_UNIFORM_SCALE |
+         AffineTransform.TYPE_GENERAL_SCALE);
+    public static int getModifiedType(AffineTransform at) {
+        int type = at.getType();
+        // Some of the vector methods can introduce a tiny uniform scale
+        // at some angles...
+        if ((type & ANY_SCALE_MASK) != 0) {
+            maxulps = Math.max(maxulps, ulps(at.getDeterminant(), 1.0));
+            if (ulps(at.getDeterminant(), 1.0) <= MAX_ULPS) {
+                // Really tiny - we will ignore it
+                type &= ~ ANY_SCALE_MASK;
+            }
+        }
+        return type;
+    }
+
+    public static boolean compare(double val1, double val2, double maxulps) {
+        if (Math.abs(val1 - val2) < 1E-15) return true;
+        return (ulps(val1, val2) <= maxulps);
+    }
+
+    public static double ulps(double val1, double val2) {
+        double diff = Math.abs(val1 - val2);
+        double ulpmax = Math.min(Math.ulp(val1), Math.ulp(val2));
+        return (diff / ulpmax);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/geom/AffineTransform/TestRotateMethods.java	Thu Oct 30 11:08:27 2014 -0700
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 4980035
+ * @summary Unit test for new methods:
+ *
+ *          AffineTransform.getRotateInstance(double x, double y);
+ *          AffineTransform.setToRotation(double x, double y);
+ *          AffineTransform.rotate(double x, double y);
+ *
+ *          AffineTransform.getQuadrantRotateInstance(int numquads);
+ *          AffineTransform.setToQuadrantRotation(int numquads);
+ *          AffineTransform.quadrantRotate(int numquads);
+ *
+ * @author flar
+ * @run main TestRotateMethods
+ */
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+
+public class TestRotateMethods {
+    /* The maximum errors allowed, measured in double precision "ulps"
+     * Note that for most fields, the tests are extremely accurate - to
+     * within 3 ulps of the smaller value in the comparison
+     * For the translation components, the tests are still very accurate,
+     * but the absolute number of ulps can be noticeably higher when we
+     * use one of the rotate methods that takes an anchor point.
+     * Since a double precision value has 56 bits of precision, even
+     * 1024 ulps is extremely small as a ratio of the value.
+     */
+    public static final double MAX_ULPS = 3.0;
+    public static final double MAX_ANCHOR_TX_ULPS = 1024.0;
+    public static double MAX_TX_ULPS = MAX_ULPS;
+
+    // Vectors for quadrant rotations
+    public static final double quadxvec[] = {  1.0,  0.0, -1.0,  0.0 };
+    public static final double quadyvec[] = {  0.0,  1.0,  0.0, -1.0 };
+
+    // Run tests once for each type of method:
+    //     tx = AffineTransform.get<Rotate>Instance()
+    //     tx.set<Rotate>()
+    //     tx.<rotate>()
+    public static enum Mode { GET, SET, MOD };
+
+    // Used to accumulate and report largest differences encountered by tests
+    public static double maxulps = 0.0;
+    public static double maxtxulps = 0.0;
+
+    // Sample anchor points for testing.
+    public static Point2D zeropt = new Point2D.Double(0, 0);
+    public static Point2D testtxpts[] = {
+        new Point2D.Double(       5,      5),
+        new Point2D.Double(      20,    -10),
+        new Point2D.Double(-Math.PI, Math.E),
+    };
+
+    public static void main(String argv[]) {
+        test(Mode.GET);
+        test(Mode.SET);
+        test(Mode.MOD);
+
+        System.out.println("Max scale and shear difference: "+maxulps+" ulps");
+        System.out.println("Max translate difference: "+maxtxulps+" ulps");
+    }
+
+    public static void test(Mode mode) {
+        MAX_TX_ULPS = MAX_ULPS; // Stricter tx testing with no anchor point
+        test(mode, 0.5, null);
+        test(mode, 1.0, null);
+        test(mode, 3.0, null);
+
+        // Anchor points make the tx values less reliable
+        MAX_TX_ULPS = MAX_ANCHOR_TX_ULPS;
+        for (int i = 0; i < testtxpts.length; i++) {
+            test(mode, 1.0, testtxpts[i]);
+        }
+        MAX_TX_ULPS = MAX_ULPS; // Restore to default
+    }
+
+    public static void verify(AffineTransform at1, AffineTransform at2,
+                              Mode mode, double vectorscale, Point2D txpt,
+                              String message, double num, String units)
+    {
+        if (!compare(at1, at2)) {
+            System.out.println("mode == "+mode);
+            System.out.println("vectorscale == "+vectorscale);
+            System.out.println("txpt == "+txpt);
+            System.out.println(at1+", type = "+at1.getType());
+            System.out.println(at2+", type = "+at2.getType());
+            System.out.println("ScaleX values differ by "+
+                               ulps(at1.getScaleX(), at2.getScaleX())+" ulps");
+            System.out.println("ScaleY values differ by "+
+                               ulps(at1.getScaleY(), at2.getScaleY())+" ulps");
+            System.out.println("ShearX values differ by "+
+                               ulps(at1.getShearX(), at2.getShearX())+" ulps");
+            System.out.println("ShearY values differ by "+
+                               ulps(at1.getShearY(), at2.getShearY())+" ulps");
+            System.out.println("TranslateX values differ by "+
+                               ulps(at1.getTranslateX(),
+                                    at2.getTranslateX())+" ulps");
+            System.out.println("TranslateY values differ by "+
+                               ulps(at1.getTranslateY(),
+                                    at2.getTranslateY())+" ulps");
+            throw new RuntimeException(message + num + units);
+        }
+    }
+
+    public static void test(Mode mode, double vectorscale, Point2D txpt) {
+        AffineTransform at1, at2, at3;
+
+        for (int deg = -720; deg <= 720; deg++) {
+            if ((deg % 90) == 0) continue;
+            double radians = Math.toRadians(deg);
+            double vecy = Math.sin(radians) * vectorscale;
+            double vecx = Math.cos(radians) * vectorscale;
+
+            at1 = makeAT(mode, txpt, radians);
+            at2 = makeAT(mode, txpt, vecx, vecy);
+            verify(at1, at2, mode, vectorscale, txpt,
+                   "vector and radians do not match for ", deg, " degrees");
+
+            if (txpt == null) {
+                // Make sure output was same as a with a 0,0 anchor point
+                if (vectorscale == 1.0) {
+                    // Only need to test radians method for one scale factor
+                    at3 = makeAT(mode, zeropt, radians);
+                    verify(at1, at3, mode, vectorscale, zeropt,
+                           "radians not invariant with 0,0 translate at ",
+                           deg, " degrees");
+                }
+                // But test vector methods with all scale factors
+                at3 = makeAT(mode, zeropt, vecx, vecy);
+                verify(at2, at3, mode, vectorscale, zeropt,
+                       "vector not invariant with 0,0 translate at ",
+                       deg, " degrees");
+            }
+        }
+
+        for (int quad = -8; quad <= 8; quad++) {
+            double degrees = quad * 90.0;
+            double radians = Math.toRadians(degrees);
+            double vecx = quadxvec[quad & 3] * vectorscale;
+            double vecy = quadyvec[quad & 3] * vectorscale;
+
+            at1 = makeAT(mode, txpt, radians);
+            at2 = makeAT(mode, txpt, vecx, vecy);
+            verify(at1, at2, mode, vectorscale, txpt,
+                   "quadrant vector and radians do not match for ",
+                   degrees, " degrees");
+            at2 = makeQuadAT(mode, txpt, quad);
+            verify(at1, at2, mode, vectorscale, txpt,
+                   "quadrant and radians do not match for ",
+                   quad, " quadrants");
+            if (txpt == null) {
+                at3 = makeQuadAT(mode, zeropt, quad);
+                verify(at2, at3, mode, vectorscale, zeropt,
+                       "quadrant not invariant with 0,0 translate at ",
+                       quad, " quadrants");
+            }
+        }
+    }
+
+    public static AffineTransform makeRandomAT() {
+        AffineTransform at = new AffineTransform();
+        at.scale(Math.random() * -10.0, Math.random() * 100.0);
+        at.rotate(Math.random() * Math.PI);
+        at.shear(Math.random(), Math.random());
+        at.translate(Math.random() * 300.0, Math.random() * -20.0);
+        return at;
+    }
+
+    public static AffineTransform makeAT(Mode mode, Point2D txpt,
+                                         double radians)
+    {
+        AffineTransform at;
+        double tx = (txpt == null) ? 0.0 : txpt.getX();
+        double ty = (txpt == null) ? 0.0 : txpt.getY();
+        switch (mode) {
+        case GET:
+            if (txpt != null) {
+                at = AffineTransform.getRotateInstance(radians, tx, ty);
+            } else {
+                at = AffineTransform.getRotateInstance(radians);
+            }
+            break;
+        case SET:
+            at = makeRandomAT();
+            if (txpt != null) {
+                at.setToRotation(radians, tx, ty);
+            } else {
+                at.setToRotation(radians);
+            }
+            break;
+        case MOD:
+            at = makeRandomAT();
+            at.setToIdentity();
+            if (txpt != null) {
+                at.rotate(radians, tx, ty);
+            } else {
+                at.rotate(radians);
+            }
+            break;
+        default:
+            throw new InternalError("unrecognized mode: "+mode);
+        }
+
+        return at;
+    }
+
+    public static AffineTransform makeAT(Mode mode, Point2D txpt,
+                                         double vx, double vy)
+    {
+        AffineTransform at;
+        double tx = (txpt == null) ? 0.0 : txpt.getX();
+        double ty = (txpt == null) ? 0.0 : txpt.getY();
+        switch (mode) {
+        case GET:
+            if (txpt != null) {
+                at = AffineTransform.getRotateInstance(vx, vy, tx, ty);
+            } else {
+                at = AffineTransform.getRotateInstance(vx, vy);
+            }
+            break;
+        case SET:
+            at = makeRandomAT();
+            if (txpt != null) {
+                at.setToRotation(vx, vy, tx, ty);
+            } else {
+                at.setToRotation(vx, vy);
+            }
+            break;
+        case MOD:
+            at = makeRandomAT();
+            at.setToIdentity();
+            if (txpt != null) {
+                at.rotate(vx, vy, tx, ty);
+            } else {
+                at.rotate(vx, vy);
+            }
+            break;
+        default:
+            throw new InternalError("unrecognized mode: "+mode);
+        }
+
+        return at;
+    }
+
+    public static AffineTransform makeQuadAT(Mode mode, Point2D txpt,
+                                             int quads)
+    {
+        AffineTransform at;
+        double tx = (txpt == null) ? 0.0 : txpt.getX();
+        double ty = (txpt == null) ? 0.0 : txpt.getY();
+        switch (mode) {
+        case GET:
+            if (txpt != null) {
+                at = AffineTransform.getQuadrantRotateInstance(quads, tx, ty);
+            } else {
+                at = AffineTransform.getQuadrantRotateInstance(quads);
+            }
+            break;
+        case SET:
+            at = makeRandomAT();
+            if (txpt != null) {
+                at.setToQuadrantRotation(quads, tx, ty);
+            } else {
+                at.setToQuadrantRotation(quads);
+            }
+            break;
+        case MOD:
+            at = makeRandomAT();
+            at.setToIdentity();
+            if (txpt != null) {
+                at.quadrantRotate(quads, tx, ty);
+            } else {
+                at.quadrantRotate(quads);
+            }
+            break;
+        default:
+            throw new InternalError("unrecognized mode: "+mode);
+        }
+
+        return at;
+    }
+
+    public static boolean compare(AffineTransform at1, AffineTransform at2) {
+        maxulps = Math.max(maxulps, ulps(at1.getScaleX(), at2.getScaleX()));
+        maxulps = Math.max(maxulps, ulps(at1.getScaleY(), at2.getScaleY()));
+        maxulps = Math.max(maxulps, ulps(at1.getShearX(), at2.getShearX()));
+        maxulps = Math.max(maxulps, ulps(at1.getShearY(), at2.getShearY()));
+        maxtxulps = Math.max(maxtxulps,
+                             ulps(at1.getTranslateX(), at2.getTranslateX()));
+        maxtxulps = Math.max(maxtxulps,
+                             ulps(at1.getTranslateY(), at2.getTranslateY()));
+        return (getModifiedType(at1) == getModifiedType(at2) &&
+                (compare(at1.getScaleX(), at2.getScaleX(), MAX_ULPS)) &&
+                (compare(at1.getScaleY(), at2.getScaleY(), MAX_ULPS)) &&
+                (compare(at1.getShearX(), at2.getShearX(), MAX_ULPS)) &&
+                (compare(at1.getShearY(), at2.getShearY(), MAX_ULPS)) &&
+                (compare(at1.getTranslateX(),
+                         at2.getTranslateX(), MAX_TX_ULPS)) &&
+                (compare(at1.getTranslateY(),
+                         at2.getTranslateY(), MAX_TX_ULPS)));
+    }
+
+    public static int getModifiedType(AffineTransform at) {
+        int type = at.getType();
+        // Some of the vector methods can introduce a tiny uniform scale
+        // at some angles...
+        if ((type & AffineTransform.TYPE_UNIFORM_SCALE) != 0) {
+            maxulps = Math.max(maxulps, ulps(at.getDeterminant(), 1.0));
+            if (ulps(at.getDeterminant(), 1.0) <= MAX_ULPS) {
+                // Really tiny - we will ignore it
+                type &= (~AffineTransform.TYPE_UNIFORM_SCALE);
+            }
+        }
+        return type;
+    }
+
+    public static boolean compare(double val1, double val2, double maxulps) {
+        return (ulps(val1, val2) <= maxulps);
+    }
+
+    public static double ulps(double val1, double val2) {
+        double diff = Math.abs(val1 - val2);
+        double ulpmax = Math.min(Math.ulp(val1), Math.ulp(val2));
+        return (diff / ulpmax);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/geom/AffineTransform/TestSerialization.java	Thu Oct 30 11:08:27 2014 -0700
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 6213608
+ * @summary Test that AffineTransform can deserialize appropriate versions.
+ * @author flar
+ * @run main TestSerialization
+ */
+
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.awt.geom.AffineTransform;
+
+public class TestSerialization {
+    public static void main(String argv[]) {
+        if (argv.length > 0) {
+            System.out.println("Saving from: "+
+                               System.getProperty("java.version"));
+            writeSer(argv[0]);
+            return;
+        }
+        System.out.println("Testing on: "+System.getProperty("java.version"));
+        testReadWrite();
+        readSer("serial.1.2", true);
+    }
+
+    public static AffineTransform testATs[] = {
+        new AffineTransform(),
+        AffineTransform.getScaleInstance(2.5, -3.0),
+        AffineTransform.getRotateInstance(Math.PI / 4.0),
+        AffineTransform.getShearInstance(1.0, -3.0),
+        AffineTransform.getTranslateInstance(25.0, 12.5),
+        makeComplexAT(),
+    };
+
+    public static AffineTransform makeComplexAT() {
+        AffineTransform at = new AffineTransform();
+        at.scale(2.5, -3.0);
+        at.rotate(Math.PI / 4.0);
+        at.shear(1.0, -3.0);
+        at.translate(25.0, 12.5);
+        return at;
+    };
+
+    public static void testReadWrite() {
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            testWrite(oos);
+            oos.flush();
+            oos.close();
+            byte buf[] = baos.toByteArray();
+            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
+            ObjectInputStream ois = new ObjectInputStream(bais);
+            testRead(ois, true);
+        } catch (InvalidClassException ice) {
+            throw new RuntimeException("Object read failed from loopback");
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException("IOException testing loopback");
+        }
+    }
+
+    public static String resolve(String relfilename) {
+        String dir = System.getProperty("test.src");
+        if (dir == null) {
+            return relfilename;
+        } else {
+            return dir+"/"+relfilename;
+        }
+    }
+
+    public static void readSer(String filename, boolean shouldsucceed) {
+        try {
+            FileInputStream fis = new FileInputStream(resolve(filename));
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            testRead(ois, shouldsucceed);
+        } catch (InvalidClassException ice) {
+            throw new RuntimeException("Object read failed from: "+filename);
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException("IOException reading: "+filename);
+        }
+    }
+
+    public static void testRead(ObjectInputStream ois, boolean shouldsucceed)
+        throws IOException
+    {
+        for (int i = 0; i < testATs.length; i++) {
+            AffineTransform at;
+            try {
+                at = (AffineTransform) ois.readObject();
+                if (!shouldsucceed) {
+                    throw new RuntimeException("readObj did not fail");
+                }
+            } catch (ClassNotFoundException e) {
+                // Should never happen, but must catch declared exceptions...
+                throw new RuntimeException("AffineTransform not found!");
+            } catch (InvalidClassException e) {
+                if (shouldsucceed) {
+                    throw e;
+                }
+                continue;
+            }
+            if (!testATs[i].equals(at)) {
+                throw new RuntimeException("wrong AT read from stream");
+            }
+        }
+    }
+
+    public static void writeSer(String filename) {
+        try {
+            FileOutputStream fos = new FileOutputStream(filename);
+            ObjectOutputStream oos = new ObjectOutputStream(fos);
+            testWrite(oos);
+            oos.flush();
+            oos.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException("IOException writing: "+filename);
+        }
+    }
+
+    public static void testWrite(ObjectOutputStream oos)
+        throws IOException
+    {
+        for (int i = 0; i < testATs.length; i++) {
+            oos.writeObject(testATs[i]);
+        }
+    }
+}
Binary file test/java/awt/geom/AffineTransform/serial.1.2 has changed