Bug 1311620 - Part 7: addition result tests per properties. r?boris draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Sat, 24 Dec 2016 19:57:49 +0900
changeset 453700 e04ace66d3543205a96cab23e0d321b334d75e18
parent 453699 9e194f4ffc7d7147eb1b4452b33587542802b510
child 453701 03353f1768393b62d4786add9b5fe1f8242cb889
push id39729
push userhiikezoe@mozilla-japan.org
push dateSat, 24 Dec 2016 11:23:17 +0000
reviewersboris
bugs1311620
milestone53.0a1
Bug 1311620 - Part 7: addition result tests per properties. r?boris This includes some failure test cases: 1) Color interpolation with overflowed colors. Additive color needs to be with overflowed color components that are produced by additive calculation. For example: an additive color animation between rgb(100, 100, 100) and rgb(200, 200, 200) on rgb(100, 100, 100) element should be an animation of colors; from rgb(200, 200, 200) to rgb(300, 300, 300). This will be fixed in the next patch (part 8). 2) All transform tests. Additive transform list has to be concatinated, it will be done in a subsequent patch (part 9). MozReview-Commit-ID: 5OoT6icVMcC
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/web-animations/animation-model/animation-types/addition-per-property.html.ini
testing/web-platform/tests/web-animations/animation-model/animation-types/addition-per-property.html
testing/web-platform/tests/web-animations/animation-model/animation-types/interpolation-per-property.html
testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -39574,16 +39574,22 @@
           }
         ],
         "uievents/order-of-events/focus-events/focus-automated-blink-webkit.html": [
           {
             "path": "uievents/order-of-events/focus-events/focus-automated-blink-webkit.html",
             "url": "/uievents/order-of-events/focus-events/focus-automated-blink-webkit.html"
           }
         ],
+        "web-animations/animation-model/animation-types/addition-per-property.html": [
+          {
+            "path": "web-animations/animation-model/animation-types/addition-per-property.html",
+            "url": "/web-animations/animation-model/animation-types/addition-per-property.html"
+          }
+        ],
         "web-animations/animation-model/animation-types/interpolation-per-property.html": [
           {
             "path": "web-animations/animation-model/animation-types/interpolation-per-property.html",
             "url": "/web-animations/animation-model/animation-types/interpolation-per-property.html"
           }
         ],
         "web-animations/animation-model/animation-types/spacing-keyframes-filters.html": [
           {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/web-animations/animation-model/animation-types/addition-per-property.html.ini
@@ -0,0 +1,59 @@
+[addition-per-property.html]
+  type: testharness
+  [background-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [border-bottom-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [border-left-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [border-right-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [border-top-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [column-rule-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [flood-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [lighting-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [outline-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [stop-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [text-decoration-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [text-emphasis-color supports animating as color of rgb() with overflowed from and to values]
+    expected: FAIL
+
+  [transform: scale]
+    expected: FAIL
+
+  [transform: skew]
+    expected: FAIL
+
+  [transform: rotate on translate]
+    expected: FAIL
+
+  [transform: translate on rotate]
+    expected: FAIL
+
+  [transform: matrix]
+    expected: FAIL
+
+  [transform: matrix3d]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/addition-per-property.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Tests for animation type of addition</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#animation-types">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<script src="property-list.js"></script>
+<script src="property-types.js"></script>
+<style>
+html {
+  font-size: 10px;
+}
+</style>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+for (var property in gCSSProperties) {
+  if (!isSupported(property)) {
+    continue;
+  }
+
+  var animationTypes = gCSSProperties[property].types;
+  var setupFunction = gCSSProperties[property].setup;
+  animationTypes.forEach(function(animationType) {
+    var typeObject;
+    var animationTypeString;
+    if (typeof animationType === 'string') {
+      typeObject = types[animationType];
+      animationTypeString = animationType;
+    } else if (typeof animationType === 'object' &&
+               animationType.type && typeof animationType.type === 'string') {
+      typeObject = types[animationType.type];
+      animationTypeString = animationType.type;
+    }
+
+    // First, test that the animation type object has 'testAddition'.
+    // We use test() function here so that we can continue the remainder tests
+    // even if this test fails.
+    test(function(t) {
+      assert_own_property(typeObject, 'testAddition', animationTypeString +
+                          ' should have testAddition property');
+      assert_equals(typeof typeObject.testAddition, 'function',
+                    'testAddition method should be a function');
+    }, animationTypeString + ' has testAddition function');
+
+    if (typeObject.testAddition &&
+        typeof typeObject.testAddition === 'function') {
+      typeObject.testAddition(property,
+                              setupFunction,
+                              animationType.options);
+    }
+  });
+}
+</script>
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/interpolation-per-property.html
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/interpolation-per-property.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
-<title>Tests for animation types</title>
+<title>Tests for animation type of interpolation</title>
 <link rel="help" href="https://w3c.github.io/web-animations/#animation-types">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../../testcommon.js"></script>
 <script src="property-list.js"></script>
 <script src="property-types.js"></script>
 <style>
 html {
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
@@ -44,16 +44,42 @@ const discreteType = {
         testAnimationSamples(animation, idlName,
                              [{ time: 0,    expected: from.toLowerCase() },
                               { time: 940,  expected: from.toLowerCase() },
                               { time: 960,  expected: to.toLowerCase() }]);
       }, property + ' uses discrete animation when animating between "'
          + from + '" and "' + to + '" with keyframe easing');
     });
   },
+
+  testAddition: function(property, setup, options) {
+    options.forEach(function(keyframes) {
+      var [ from, to ] = keyframes;
+      test(function(t) {
+        var idlName = propertyToIDL(property);
+        var target = createTestElement(t, setup);
+        target.animate({ [idlName]: [from, from] }, 1000);
+        var animation = target.animate({ [idlName]: [to, to] },
+                                       { duration: 1000, composite: 'add' });
+        testAnimationSamples(animation, idlName,
+                             [{ time: 0, expected: to.toLowerCase() }]);
+      }, property + ': "' + to + '" onto "' + from + '"');
+
+      test(function(t) {
+        var idlName = propertyToIDL(property);
+        var target = createTestElement(t, setup);
+        target.animate({ [idlName]: [to, to] }, 1000);
+        var animation = target.animate({ [idlName]: [from, from] },
+                                       { duration: 1000, composite: 'add' });
+        testAnimationSamples(animation, idlName,
+                             [{ time: 0, expected: from.toLowerCase() }]);
+      }, property + ': "' + from + '" onto "' + to + '"');
+    });
+  },
+
 };
 
 const lengthType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['10px', '50px'] },
@@ -70,46 +96,92 @@ const lengthType = {
       var animation = target.animate({ [idlName]: ['1rem', '5rem'] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: '10px' },
                             { time: 500,  expected: '30px' },
                             { time: 1000, expected: '50px' }]);
     }, property + ' supports animating as a length of rem');
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '10px';
+      var animation = target.animate({ [idlName]: ['10px', '50px'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px' }]);
+    }, property + ': length');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '1rem';
+      var animation = target.animate({ [idlName]: ['1rem', '5rem'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px' }]);
+    }, property + ': length of rem');
+  },
+
 };
 
 const percentageType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['10%', '50%'] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: '10%' },
                             { time: 500,  expected: '30%' },
                             { time: 1000, expected: '50%' }]);
     }, property + ' supports animating as a percentage');
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '60%';
+      var animation = target.animate({ [idlName]: ['70%', '100%'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName, [{ time: 0, expected: '130%' }]);
+    }, property + ': percentage');
+  },
+
 };
 
 const integerType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: [-2, 2] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: '-2' },
                             { time: 500,  expected: '0' },
                             { time: 1000, expected: '2' }]);
     }, property + ' supports animating as an integer');
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = -1;
+      var animation = target.animate({ [idlName]: [-2, 2] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,    expected: '-3' }]);
+    }, property + ': integer');
+  },
+
 };
 
 const lengthPercentageOrCalcType = {
   testInterpolation: function(property, setup) {
     lengthType.testInterpolation(property, setup);
     percentageType.testInterpolation(property, setup);
 
     test(function(t) {
@@ -166,31 +238,129 @@ const lengthPercentageOrCalcType = {
                            [{ time: 0,
                               expected: 'calc(10px + 10%)' },
                             { time: 500,
                               expected: 'calc(15px + 15%)' },
                             { time: 1000,
                               expected: 'calc(20px + 20%)' }]);
     }, property + ' supports animating as a calc');
   },
+
+  testAddition: function(property, setup) {
+    lengthType.testAddition(property, setup);
+    percentageType.testAddition(property, setup);
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '10px';
+      var animation = target.animate({ [idlName]: ['10%', '50%'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0, expected: 'calc(10px + 10%)' }]);
+    }, property + ': units "%" onto "px"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '10%';
+      var animation = target.animate({ [idlName]: ['10px', '50px'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0, expected: 'calc(10px + 10%)' }]);
+    }, property + ': units "px" onto "%"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '10%';
+      var animation = target.animate({ [idlName]: ['2rem', '5rem'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0, expected: 'calc(20px + 10%)' }]);
+    }, property + ': units "rem" onto "%"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '2rem';
+      var animation = target.animate({ [idlName]: ['10%', '50%'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0, expected: 'calc(20px + 10%)' }]);
+    }, property + ': units "%" onto "rem"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '2em';
+      var animation = target.animate({ [idlName]: ['2rem', '5rem'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName, [{ time: 0, expected: '40px' }]);
+    }, property + ': units "rem" onto "em"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '2rem';
+      var animation = target.animate({ [idlName]: ['2em', '5em'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName, [{ time: 0, expected: '40px' }]);
+    }, property + ': units "em" onto "rem"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = '10px';
+      var animation = target.animate({ [idlName]: ['calc(2em + 20%)',
+                                                   'calc(5rem + 50%)'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0, expected: 'calc(30px + 20%)' }]);
+    }, property + ': units "calc" onto "px"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'calc(10px + 10%)';
+      var animation = target.animate({ [idlName]: ['calc(20px + 20%)',
+                                                   'calc(2em + 3rem + 40%)'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0, expected: 'calc(30px + 30%)' }]);
+    }, property + ': calc');
+  },
+
 };
 
 const positiveNumberType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: [1.1, 1.5] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: '1.1' },
                             { time: 500,  expected: '1.3' },
                             { time: 1000, expected: '1.5' }]);
     }, property + ' supports animating as a positive number');
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 1.1;
+      var animation = target.animate({ [idlName]: [1.1, 1.5] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName, [{ time: 0, expected: '2.2' }]);
+    }, property + ': positive number');
+  },
+
 };
 
 const visibilityType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['visible', 'hidden'] },
@@ -243,16 +413,43 @@ const visibilityType = {
                             { time: 330,  expected: 'visible' },
                             { time: 340,  expected: 'visible' },
                             { time: 620,  expected: 'visible' },
                             { time: 630,  expected: 'hidden' },
                             { time: 1000, expected: 'hidden' }]);
     }, property + ' uses visibility animation when animating '
      + 'from "visible" to "hidden" with easeInOutBack easing');
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'visible';
+      var animation = target.animate({ [idlName]: ['visible', 'hidden'] },
+                                     { duration: 1000, fill: 'both',
+                                       composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,    expected: 'visible' },
+                            { time: 1000, expected: 'visible' }]);
+    }, property + ': onto "visible"');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'hidden';
+      var animation = target.animate({ [idlName]: ['hidden', 'visible'] },
+                                     { duration: 1000, fill: 'both',
+                                       composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,    expected: 'hidden' },
+                            { time: 1000, expected: 'visible' }]);
+    }, property + ': onto "hidden"');
+  },
+
 };
 
 const colorType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['rgb(255, 0, 0)',
@@ -307,16 +504,88 @@ const colorType = {
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['hsla(0,   100%, 50%, 0.4)',
                                                    'hsla(240, 100%, 50%, 0.8)'] },
                                      1000);
       testAnimationSamples(animation, idlName,      // Same as above.
                            [{ time: 500,  expected: 'rgba(85, 0, 170, 0.6)' }]);
     }, property + ' supports animating as color of hsla()');
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(128, 128, 128)';
+      var animation = target.animate({ [idlName]: ['rgb(255, 0, 0)',
+                                                   'rgb(0, 0, 255)'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,   expected: 'rgb(255, 128, 128)' },
+                            // The value at 50% is interpolated
+                            // from 'rgb(128+255, 128, 128)'
+                            // to   'rgb(128,     128, 128+255)'.
+                            { time: 500, expected: 'rgb(255, 128, 255)' }]);
+    }, property + ' supports animating as color of rgb() with overflowed ' +
+       'from and to values');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(128, 128, 128)';
+      var animation = target.animate({ [idlName]: ['#ff0000', '#0000ff'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,  expected: 'rgb(255, 128, 128)' }]);
+    }, property + ' supports animating as color of #RGB');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(128, 128, 128)';
+      var animation = target.animate({ [idlName]: ['hsl(0,   100%, 50%)',
+                                                   'hsl(240, 100%, 50%)'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,  expected: 'rgb(255, 128, 128)' }]);
+    }, property + ' supports animating as color of hsl()');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(128, 128, 128)';
+      var animation = target.animate({ [idlName]: ['#ff000066', '#0000ffcc'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,
+                           [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
+    }, property + ' supports animating as color of #RGBa');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(128, 128, 128)';
+      var animation = target.animate({ [idlName]: ['rgba(255, 0, 0, 0.4)',
+                                                   'rgba(0, 0, 255, 0.8)'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,      // Same as above.
+                           [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
+    }, property + ' supports animating as color of rgba()');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(128, 128, 128)';
+      var animation = target.animate({ [idlName]: ['hsla(0,   100%, 50%, 0.4)',
+                                                   'hsla(240, 100%, 50%, 0.8)'] },
+                                     { duration: 1000, composite: 'add' });
+      testAnimationSamples(animation, idlName,      // Same as above.
+                           [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
+    }, property + ' supports animating as color of hsla()');
+  },
+
 };
 
 const transformListType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['translate(200px, -200px)',
@@ -452,16 +721,182 @@ const transformListType = {
       var animation =
         target.animate({ [idlName]: [ from, to ] }, 1000);
 
       testAnimationSampleMatrices(animation, idlName,
         [{ time: 500, expected: rotate3dToMatrix(1, 1, 0, Math.PI * 2 / 4) }]);
     }, property + ': matrix3d');
 
   },
+
+  testAddition: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'translateX(100px)';
+      var animation = target.animate({ [idlName]: ['translateX(-200px)',
+                                                   'translateX(500px)'] },
+                                     { duration: 1000, fill: 'both',
+                                       composite: 'add' });
+      testAnimationSampleMatrices(animation, idlName,
+        [ { time: 0,    expected: [ 1, 0, 0, 1, -100, 0 ] },
+          { time: 1000, expected: [ 1, 0, 0, 1,  600, 0 ] }]);
+    }, property + ': translate');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rotate(45deg)';
+      var animation = target.animate({ [idlName]: ['rotate(-90deg)',
+                                                   'rotate(90deg)'] },
+                                     { duration: 1000, fill: 'both',
+                                       composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ Math.cos(-Math.PI / 4),
+                                   Math.sin(-Math.PI / 4),
+                                  -Math.sin(-Math.PI / 4),
+                                   Math.cos(-Math.PI / 4),
+                                   0, 0] },
+         { time: 1000, expected: [ Math.cos(Math.PI * 3 / 4),
+                                   Math.sin(Math.PI * 3 / 4),
+                                  -Math.sin(Math.PI * 3 / 4),
+                                   Math.cos(Math.PI * 3 / 4),
+                                   0, 0] }]);
+    }, property + ': rotate');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'scale(2)';
+      var animation = target.animate({ [idlName]: ['scale(-3)', 'scale(5)'] },
+                                     { duration: 1000, fill: 'both',
+                                       composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ -6, 0, 0, -6, 0, 0 ] }, // scale(-3) scale(2)
+         { time: 1000, expected: [ 10, 0, 0, 10, 0, 0 ] }]); // scale(5) scale(2)
+    }, property + ': scale');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+                              // matrix(1, tan(10deg), tan(10deg), 1)
+      target.style[idlName] = 'skew(10deg, 10deg)';
+      var animation =                // matrix(1, tan(20deg), tan(-30deg), 1)
+        target.animate({ [idlName]: ['skew(-30deg, 20deg)',
+                                     // matrix(1, tan(-30deg), tan(20deg), 1)
+                                     'skew(20deg, -30deg)'] },
+                       { duration: 1000, fill: 'both', composite: 'add' });
+
+      // matrix at 0%.
+      // [ 1          tan(10deg) ] [ 1          tan(-30deg) ]
+      // [ tan(10deg)          1 ] [ tan(20deg)           1 ] =
+      //
+      // [ 1 + tan(10deg) * tan(20deg) tan(-30deg) + tan(10deg)     ]
+      // [     tan(10deg) + tan(20deg) tan(10deg) * tan(-30deg) + 1 ]
+
+      // matrix at 100%.
+      // [ 1          tan(10deg) ] [ 1           tan(20deg) ]
+      // [ tan(10deg)          1 ] [ tan(-30deg)          1 ] =
+      //
+      // [ 1 + tan(10deg) * tan(-30deg) tan(20deg) + tan(10deg)     ]
+      // [     tan(10deg) + tan(-30deg) tan(10deg) * tan(20deg) + 1 ]
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ 1 + Math.tan(Math.PI/18) * Math.tan(Math.PI/9),
+                                   Math.tan(Math.PI/18) + Math.tan(Math.PI/9),
+                                   Math.tan(-Math.PI/6) + Math.tan(Math.PI/18),
+                                   1 + Math.tan(Math.PI/18) * Math.tan(-Math.PI/6),
+                                   0, 0] },
+         { time: 1000, expected: [ 1 + Math.tan(Math.PI/18) * Math.tan(-Math.PI/6),
+                                   Math.tan(Math.PI/18) + Math.tan(-Math.PI/6),
+                                   Math.tan(Math.PI/9) + Math.tan(Math.PI/18),
+                                   1 + Math.tan(Math.PI/18) * Math.tan(Math.PI/9),
+                                   0, 0] }]);
+    }, property + ': skew');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+                               // matrix(1, 0, 0, 1, 100, 0)
+      target.style[idlName] = 'translateX(100px)';
+      var animation =                // matrix(0, 1, -1, 0, 0, 0)
+        target.animate({ [idlName]: ['rotate(90deg)',
+                                     // matrix(-1, 0, 0, -1, 0, 0)
+                                     'rotate(180deg)'] },
+                       { duration: 1000, fill: 'both', composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [  0, 1, -1,  0, 100, 0 ] },
+         { time: 1000, expected: [ -1, 0,  0, -1, 100, 0 ] }]);
+    }, property + ': rotate on translate');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+                               // matrix(0, 1, -1, 0, 0, 0)
+      target.style[idlName] = 'rotate(90deg)';
+      var animation =                // matrix(1, 0, 0, 1, 100, 0)
+        target.animate({ [idlName]: ['translateX(100px)',
+                                     // matrix(1, 0, 0, 1, 200, 0)
+                                     'translateX(200px)'] },
+                       { duration: 1000, fill: 'both', composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ 0, 1, -1, 0, 0, 100 ] },
+         { time: 1000, expected: [ 0, 1, -1, 0, 0, 200 ] }]);
+    }, property + ': translate on rotate');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'matrix(0, 1, -1, 0, 0, 0)';
+      var animation =                 // Same matrices as above.
+        target.animate({ [idlName]: [ 'matrix(1, 0, 0, 1, 100, 0)',
+                                      'matrix(1, 0, 0, 1, 200, 0)' ] },
+                       { duration: 1000, fill: 'both', composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ 0, 1, -1, 0, 0, 100 ] },
+         { time: 1000, expected: [ 0, 1, -1, 0, 0, 200 ] }]);
+    }, property + ': matrix');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rotate3d(1, 1, 0, 45deg)';
+      var animation =
+        target.animate({ [idlName]: [ 'rotate3d(1, 1, 0, -90deg)',
+                                      'rotate3d(1, 1, 0, 90deg)'] },
+                       { duration: 1000, fill: 'both', composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: rotate3dToMatrix(1, 1, 0,    -Math.PI / 4) },
+         { time: 1000, expected: rotate3dToMatrix(1, 1, 0, 3 * Math.PI / 4) }]);
+    }, property + ': rotate3d');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      // To calculate expected matrices easily, generate input matrices from
+      // rotate3d.
+      target.style[idlName] = rotate3dToMatrix3d(1, 1, 0, Math.PI / 4);
+      var from = rotate3dToMatrix3d(1, 1, 0, -Math.PI / 2);
+      var to = rotate3dToMatrix3d(1, 1, 0, Math.PI / 2);
+      var animation =
+        target.animate({ [idlName]: [ from, to ] },
+                       { duration: 1000, fill: 'both', composite: 'add' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: rotate3dToMatrix(1, 1, 0,    -Math.PI / 4) },
+         { time: 1000, expected: rotate3dToMatrix(1, 1, 0, 3 * Math.PI / 4) }]);
+    }, property + ': matrix3d');
+  },
+
 };
 
 const types = {
   color: colorType,
   discrete: discreteType,
   integer: integerType,
   length: lengthType,
   percentage: percentageType,