Bug 1364795 - Add tests for accumulation per property. r?boris draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Mon, 15 May 2017 13:24:01 +0900
changeset 577618 3ecffdc18fac6dc196c0c37e25b0d9b0b0e4ff9e
parent 577609 992f0940cac61d13073821c7a1854ecbb1b0f933
child 628545 2a92885c00e7515876c4a76bf2ee8a5990e15e33
push id58739
push userhikezoe@mozilla.com
push dateMon, 15 May 2017 04:24:57 +0000
reviewersboris
bugs1364795
milestone55.0a1
Bug 1364795 - Add tests for accumulation per property. r?boris MozReview-Commit-ID: GTa48O3qn2H
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/web-animations/animation-model/animation-types/accumulation-per-property.html.ini
testing/web-platform/tests/web-animations/animation-model/animation-types/accumulation-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
@@ -128925,16 +128925,22 @@
     ]
    ],
    "vibration/silent-ignore.html": [
     [
      "/vibration/silent-ignore.html",
      {}
     ]
    ],
+   "web-animations/animation-model/animation-types/accumulation-per-property.html": [
+    [
+     "/web-animations/animation-model/animation-types/accumulation-per-property.html",
+     {}
+    ]
+   ],
    "web-animations/animation-model/animation-types/addition-per-property.html": [
     [
      "/web-animations/animation-model/animation-types/addition-per-property.html",
      {}
     ]
    ],
    "web-animations/animation-model/animation-types/discrete-animation.html": [
     [
@@ -218726,16 +218732,20 @@
   "web-animations/OWNERS": [
    "c4f52fc673833f80178284b30d6fc4bad1f581d2",
    "support"
   ],
   "web-animations/README.md": [
    "d6cb0e31dc3cc6d83b5051cee38a0b8e118fd43f",
    "support"
   ],
+  "web-animations/animation-model/animation-types/accumulation-per-property.html": [
+   "316dd15ce99cbfb5754c236210ac9e04dfc0c7ba",
+   "testharness"
+  ],
   "web-animations/animation-model/animation-types/addition-per-property.html": [
    "24fae46b5f7b47e1d098fa446037bf9cc52e050e",
    "testharness"
   ],
   "web-animations/animation-model/animation-types/discrete-animation.html": [
    "eee8ff07b3ec5e83e5f18305f5bc00eb72468443",
    "testharness"
   ],
@@ -218743,17 +218753,17 @@
    "55100f7d505bc8cbc966ced0d1337ed78534a553",
    "testharness"
   ],
   "web-animations/animation-model/animation-types/property-list.js": [
    "83a52204cc36f6b757129dae947f03f6a8748bde",
    "support"
   ],
   "web-animations/animation-model/animation-types/property-types.js": [
-   "929b27ecd1255765bfa35acdabe1a2c92e6998d1",
+   "66c606f9451cc30ba6d23bcfffcb61871283b0b8",
    "support"
   ],
   "web-animations/animation-model/animation-types/spacing-keyframes-filters.html": [
    "bd771a8a18245560221d92ea3495f81918c09848",
    "testharness"
   ],
   "web-animations/animation-model/animation-types/spacing-keyframes-shapes.html": [
    "03c3ab6815cfa96c07d5f95b6059fb276c50a25f",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/web-animations/animation-model/animation-types/accumulation-per-property.html.ini
@@ -0,0 +1,5 @@
+[accumulation-per-property.html]
+  type: testharness
+  [column-gap: "normal" onto "200px"]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/accumulation-per-property.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Tests for animation type of accumulation</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 'testAccumulation'.
+    // 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, 'testAccumulation', animationTypeString +
+                          ' should have testAccumulation property');
+      assert_equals(typeof typeObject.testAccumulation, 'function',
+                    'testAccumulation method should be a function');
+    }, property + ' (type: ' + animationTypeString +
+       ') has testAccumulation function');
+
+    if (typeObject.testAccumulation &&
+        typeof typeObject.testAccumulation === 'function') {
+      typeObject.testAccumulation(property,
+                              setupFunction,
+                              animationType.options);
+    }
+  });
+}
+</script>
--- 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
@@ -45,41 +45,48 @@ const discreteType = {
                              [{ 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) {
+  testAdditionOrAccumulation: function(property, setup, options, composite) {
     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' });
+                                       { duration: 1000, composite: composite });
         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' });
+                                       { duration: 1000, composite: composite });
         testAnimationSamples(animation, idlName,
                              [{ time: 0, expected: from.toLowerCase() }]);
       }, property + ': "' + from + '" onto "' + to + '"');
     });
   },
 
+  testAddition: function(property, setup, options) {
+    this.testAdditionOrAccumulation(property, setup, options, 'add');
+  },
+
+  testAccumulation: function(property, setup, options) {
+    this.testAdditionOrAccumulation(property, setup, options, 'accumulate');
+  },
 };
 
 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'] },
@@ -93,36 +100,43 @@ const lengthType = {
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['1rem', '5rem'] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 500,  expected: '30px' }]);
     }, property + ' supports animating as a length of rem');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                     { duration: 1000, composite: composite});
       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' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px' }]);
     }, property + ': length of rem');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 const lengthPairType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['10px 10px', '50px 50px'] },
@@ -136,113 +150,141 @@ const lengthPairType = {
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['1rem 1rem', '5rem 5rem'] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 500,  expected: '30px 30px' }]);
     }, property + ' supports animating as a length pair of rem');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = '10px 10px';
       var animation = target.animate({ [idlName]: ['10px 10px', '50px 50px'] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px 20px' }]);
     }, property + ': length pair');
 
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = '1rem 1rem';
       var animation = target.animate({ [idlName]: ['1rem 1rem', '5rem 5rem'] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px 20px' }]);
     }, property + ': length pair of rem');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 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: 500,  expected: '30%' }]);
     }, property + ' supports animating as a percentage');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '130%' }]);
     }, property + ': percentage');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 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: 500,  expected: '0' }]);
     }, property + ' supports animating as an integer');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: '-3' }]);
     }, property + ': integer');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 const positiveIntegerType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: [1, 3] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [ { time: 500,  expected: '2' } ]);
     }, property + ' supports animating as a positive integer');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 1;
       var animation = target.animate({ [idlName]: [2, 5] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: '3' }]);
     }, property + ': positive integer');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 const lengthPercentageOrCalcType = {
   testInterpolation: function(property, setup) {
     lengthType.testInterpolation(property, setup);
     percentageType.testInterpolation(property, setup);
 
     test(function(t) {
@@ -288,161 +330,182 @@ const lengthPercentageOrCalcType = {
         { [idlName]: ['calc(10px + 10%)', 'calc(1em + 1rem + 20%)'] },
         { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 500,
                               expected: 'calc(15px + 15%)' }]);
     }, property + ' supports animating as a calc');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName,
                            [{ time: 0, expected: 'calc(30px + 30%)' }]);
     }, property + ': calc');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 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: 500,  expected: '1.3' }]);
     }, property + ' supports animating as a positive number');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '2.2' }]);
     }, property + ': positive number');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 // Test using float values in the range [0, 1]
 const opacityType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: [0.3, 0.8] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(animation, idlName,
                            [{ time: 500,  expected: '0.55' }]);
     }, property + ' supports animating as a [0, 1] number');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 0.3;
       var animation = target.animate({ [idlName]: [0.3, 0.8] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '0.6' }]);
     }, property + ': [0, 1] number');
 
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 0.8;
       var animation = target.animate({ [idlName]: [0.3, 0.8] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName, [{ time: 0, expected: '1' }]);
     }, property + ': [0, 1] number (clamped)');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 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'] },
@@ -496,42 +559,49 @@ const visibilityType = {
                             { 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) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                       composite: composite });
       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' });
+                                       composite: composite });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: 'hidden' },
                             { time: 1000, expected: 'visible' }]);
     }, property + ': onto "hidden"');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 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)',
@@ -587,87 +657,94 @@ const colorType = {
       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) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       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' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(animation, idlName,      // Same as above.
                            [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
     }, property + ' supports animating as color of hsla()');
   },
 
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 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)',
@@ -969,16 +1046,164 @@ const transformListType = {
                        { 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');
   },
 
+  testAccumulation: 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: 'accumulate' });
+      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: 'accumulate' });
+
+      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: 'accumulate' });
+
+      testAnimationSampleMatrices(animation, idlName,
+                                  // scale((2 - 1) + (-3 - 1) + 1)
+        [{ time: 0,    expected: [ -2, 0, 0, -2, 0, 0 ] },
+                                  // scale((2 - 1) + (5 - 1) + 1)
+         { time: 1000, expected: [  6, 0, 0,  6, 0, 0 ] }]);
+    }, 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: 'accumulate' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ 1, Math.tan(Math.PI/6),
+                                   Math.tan(-Math.PI/9), 1,
+                                   0, 0] },
+         { time: 1000, expected: [ 1, Math.tan(-Math.PI/9),
+                                   Math.tan(Math.PI/6), 1,
+                                   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: 'accumulate' });
+
+      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: 'accumulate' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ 0, 1, -1, 0, 100, 0 ] },
+         { time: 1000, expected: [ 0, 1, -1, 0, 200, 0 ] }]);
+    }, 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: 'accumulate' });
+
+      testAnimationSampleMatrices(animation, idlName,
+        [{ time: 0,    expected: [ 0, 1, -1, 0, 100, 0 ] },
+         { time: 1000, expected: [ 0, 1, -1, 0, 200, 0 ] }]);
+    }, 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: 'accumulate' });
+
+      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: 'accumulate' });
+
+      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 filterListType = {
   testAddition: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'blur(10px)';
@@ -995,16 +1220,44 @@ const filterListType = {
       target.style[idlName] = 'blur(10px)';
       var animation = target.animate({ [idlName]: ['brightness(80%)',
                                                    'brightness(40%)'] },
                                      { duration: 1000, composite: 'add' });
       testAnimationSamples(animation, idlName,
         [ { time: 0,    expected: 'blur(10px) brightness(0.8)' }]);
     }, property + ': different filter functions');
   },
+
+  testAccumulation: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'blur(10px) brightness(0.3)';
+      var animation = target.animate({ [idlName]: ['blur(20px) brightness(0.1)',
+                                                   'blur(20px) brightness(0.1)'] },
+                                     { duration: 1000, composite: 'accumulate' });
+      // brightness(0.1) onto brightness(0.3) means
+      // brightness((0.1 - 1.0) + (0.3 - 1.0) + 1.0). The result of this formula
+      // is brightness(-0.6) that means brightness(0.0).
+      testAnimationSamples(animation, idlName,
+        [ { time: 0,    expected: 'blur(30px) brightness(0)' }]);
+    }, property + ': same ordered filter functions');
+
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'blur(10px) brightness(1.3)';
+      var animation = target.animate({ [idlName]: ['brightness(1.2) blur(20px)',
+                                                   'brightness(1.2) blur(20px)'] },
+                                     { duration: 1000, composite: 'accumulate' });
+      // Mismatched ordered functions can't be accumulated.
+      testAnimationSamples(animation, idlName,
+        [ { time: 0,    expected: 'brightness(1.2) blur(20px)' }]);
+    }, property + ': mismatched ordered filter functions');
+  },
 };
 
 const textShadowListType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation =
@@ -1089,16 +1342,30 @@ const textShadowListType = {
         target.animate({ [idlName]: [ 'rgb(120, 120, 120) 10px 10px 10px',
                                       'rgb(120, 120, 120) 10px 10px 10px'] },
                        { duration: 1000, composite: 'add' });
       testAnimationSamples(animation, idlName,
         [ { time: 0, expected: 'rgb(0, 0, 0) 0px 0px 0px, ' +
                                'rgb(120, 120, 120) 10px 10px 10px' }]);
     }, property + ': shadow');
   },
+
+  testAccumulation: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(120, 120, 120) 10px 10px 10px';
+      var animation =
+        target.animate({ [idlName]: [ 'rgb(120, 120, 120) 10px 10px 10px',
+                                      'rgb(120, 120, 120) 10px 10px 10px'] },
+                       { duration: 1000, composite: 'accumulate' });
+      testAnimationSamples(animation, idlName,
+        [ { time: 0, expected: 'rgb(240, 240, 240) 20px 20px 20px' }]);
+    }, property + ': shadow');
+  },
 };
 
 
 const boxShadowListType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
@@ -1184,16 +1451,30 @@ const boxShadowListType = {
         target.animate({ [idlName]: [ 'rgb(120, 120, 120) 10px 10px 10px 0px',
                                       'rgb(120, 120, 120) 10px 10px 10px 0px'] },
                        { duration: 1000, composite: 'add' });
       testAnimationSamples(animation, idlName,
         [ { time: 0, expected: 'rgb(0, 0, 0) 0px 0px 0px 0px, ' +
                                'rgb(120, 120, 120) 10px 10px 10px 0px' }]);
     }, property + ': shadow');
   },
+
+  testAccumulation: function(property, setup) {
+    test(function(t) {
+      var idlName = propertyToIDL(property);
+      var target = createTestElement(t, setup);
+      target.style[idlName] = 'rgb(120, 120, 120) 10px 10px 10px 10px';
+      var animation =
+        target.animate({ [idlName]: [ 'rgb(120, 120, 120) 10px 10px 10px 10px',
+                                      'rgb(120, 120, 120) 10px 10px 10px 10px'] },
+                       { duration: 1000, composite: 'accumulate' });
+      testAnimationSamples(animation, idlName,
+        [ { time: 0, expected: 'rgb(240, 240, 240) 20px 20px 20px 20px' }]);
+    }, property + ': shadow');
+  },
 };
 
 const positionType = {
   testInterpolation: function(property, setup) {
     lengthPairType.testInterpolation(property, setup);
 
     test(function(t) {
       var idlName = propertyToIDL(property);
@@ -1201,30 +1482,38 @@ const positionType = {
       var animation = target.animate({ [idlName]: ['10% 10%', '50% 50%'] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(
         animation, idlName,
         [{ time: 500,  expected: calcFromPercentage(idlName, '30% 30%') }]);
     }, property + ' supports animating as a position of percent');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     lengthPairType.testAddition(property, setup);
 
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = '60% 60%';
       var animation = target.animate({ [idlName]: ['70% 70%', '100% 100%'] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(
         animation, idlName,
         [{ time: 0, expected: calcFromPercentage(idlName, '130% 130%') }]);
     }, property + ': position of percentage');
   },
+
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 };
 
 const rectType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]:
@@ -1232,30 +1521,38 @@ const rectType = {
                                           'rect(50px, 50px, 50px, 50px)'] },
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(
           animation, idlName,
           [{ time: 500,  expected: 'rect(30px, 30px, 30px, 30px)' }]);
     }, property + ' supports animating as a rect');
   },
 
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rect(100px, 100px, 100px, 100px)';
       var animation = target.animate({ [idlName]:
                                          ['rect(10px, 10px, 10px, 10px)',
                                           'rect(10px, 10px, 10px, 10px)'] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(
         animation, idlName,
         [{ time: 0, expected: 'rect(110px, 110px, 110px, 110px)' }]);
     }, property + ': rect');
   },
+
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 }
 
 // stroke-dasharray: none | [ <length> | <percentage> | <number> ]*
 const dasharrayType = {
   testInterpolation: function(property, setup) {
     percentageType.testInterpolation(property, setup);
     positiveNumberType.testInterpolation(property, setup);
 
@@ -1280,33 +1577,42 @@ const dasharrayType = {
                                      { duration: 1000, fill: 'both' });
       testAnimationSamples(
           animation, idlName,
           [{ time: 500,  expected: '4, 40%, 4, 6' }]);
     }, property + ' supports animating as a dasharray (mixed number and percentage)');
 
   },
 
-  // Note that stroke-dasharray is non-additive property, so we should write this
-  // additive test case that animating value replaces underlying values.
+  // Note that stroke-dasharray is neither additive nor cumulative, so we should
+  // write this additive test case that animating value replaces underlying
+  // values.
   // See https://www.w3.org/TR/SVG2/painting.html#StrokeDashing.
-  testAddition: function(property, setup) {
+  testAdditionOrAccumulation: function(property, setup, composite) {
     test(function(t) {
       var idlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = '6, 30%, 2px';
       var animation = target.animate({ [idlName]:
                                          ['1, 2, 3, 4, 5',
                                           '6, 7, 8, 9, 10'] },
-                                     { duration: 1000, composite: 'add' });
+                                     { duration: 1000, composite: composite });
       testAnimationSamples(
           animation, idlName,
           [{ time: 0, expected: '1, 2, 3, 4, 5' }]);
     }, property + ': dasharray');
   },
+
+  testAddition: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'add');
+  },
+
+  testAccumulation: function(property, setup) {
+    this.testAdditionOrAccumulation(property, setup, 'accumulate');
+  },
 }
 
 const types = {
   color: colorType,
   discrete: discreteType,
   filterList: filterListType,
   integer: integerType,
   positiveInteger: positiveIntegerType,