21504d3adf29855b903ae38901ec2eff6e577d53
[WebKit-https.git] / LayoutTests / animations / animation-test-helpers.js
1 /* This is the helper function to run animation tests:
2
3 Test page requirements:
4 - The body must contain an empty div with id "result"
5 - Call this function directly from the <script> inside the test page
6
7 Function parameters:
8     expected [required]: an array of arrays defining a set of CSS properties that must have given values at specific times (see below)
9     callback [optional]: a function to be executed just before the test starts (none by default)
10     event [optional]: which DOM event to wait for before starting the test ("webkitAnimationStart" by default)
11
12     Each sub-array must contain these items in this order:
13     - the name of the CSS animation (may be null) [1]
14     - the time in seconds at which to snapshot the CSS property
15     - the id of the element on which to get the CSS property value [2]
16     - the name of the CSS property to get [3]
17     - the expected value for the CSS property
18     - the tolerance to use when comparing the effective CSS property value with its expected value
19
20     [1] If null is passed, a regular setTimeout() will be used instead to snapshot the animated property in the future,
21     instead of fast forwarding using the pauseAnimationAtTimeOnElementWithId() JS API from DRT
22     
23     [2] If a single string is passed, it is the id of the element to test. If an array with 2 elements is passed they
24     are the ids of 2 elements, whose values are compared for equality. In this case the expected value is ignored
25     but the tolerance is used in the comparison.
26
27     [3] If the CSS property name is "webkitTransform", expected value must be an array of 1 or more numbers corresponding to the matrix elements,
28     or a string which will be compared directly (useful if the expected value is "none")
29     If the CSS property name is "webkitTransform.N", expected value must be a number corresponding to the Nth element of the matrix
30
31 */
32 function runAnimationTest(expected, callback, event)
33 {
34     var result = "";
35     var hasPauseAnimationAPI = ('layoutTestController' in window) && ('pauseAnimationAtTimeOnElementWithId' in layoutTestController);
36     
37     function isCloseEnough(actual, desired, tolerance)
38     {
39         var diff = Math.abs(actual - desired);
40         return diff <= tolerance;
41     }
42     
43     function matrixStringToArray(s)
44     {
45         var m = s.split("(");
46         m = m[1].split(")");
47         return m[0].split(",");
48     }
49     
50     function checkExpectedValue(expected, index)
51     {
52         var animationName = expected[index][0];
53         var time = expected[index][1];
54         var elementId = expected[index][2];
55         var property = expected[index][3];
56         var expectedValue = expected[index][4];
57         var tolerance = expected[index][5];
58
59         // Check for a pair of element Ids
60         var compareElements = false;
61         var elementId2;
62         if (typeof elementId != "string") {
63             if (elementId.length != 2)
64                 return;
65                 
66             elementId2 = elementId[1];
67             elementId = elementId[0];
68             compareElements = true;
69         }
70
71         if (animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId)) {
72             result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
73             return;
74         }
75         
76         if (compareElements && animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId2)) {
77             result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
78             return;
79         }
80         
81         var computedValue, computedValue2;
82         var pass = true;
83         if (!property.indexOf("webkitTransform")) {
84             computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform;
85             if (compareElements) {
86                 computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitTransform;
87                 var m1 = matrixStringToArray(computedValue);
88                 var m2 = matrixStringToArray(computedValue2);
89                 
90                 // for now we assume that both matrices are either both 2D or both 3D
91                 var count = (computedValue.substring(0, 7) == "matrix3d") ? 16 : 6;
92                 for (var i = 0; i < count; ++i) {
93                     pass = isCloseEnough(parseFloat(m1[i]), m2[i], tolerance);
94                     if (!pass)
95                         break;
96                 }                
97             } else {
98                 if (typeof expectedValue == "string")
99                     pass = (computedValue == expectedValue);
100                 else if (typeof expectedValue == "number") {
101                     var m = matrixStringToArray(computedValue);
102                     pass = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
103                 } else {
104                     var m = matrixStringToArray(computedValue);
105                     for (i = 0; i < expectedValue.length; ++i) {
106                         pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
107                         if (!pass)
108                             break;
109                     }
110                 }
111             }
112         } else if (property == "lineHeight") {
113             computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight);
114             if (compareElements) {
115                 computedValue2 = parseInt(window.getComputedStyle(document.getElementById(elementId2)).lineHeight);
116                 pass = isCloseEnough(computedValue, computedValue2, tolerance);
117             }
118             else
119                 pass = isCloseEnough(computedValue, expectedValue, tolerance);
120         } else {
121             var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property);
122             computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
123             if (compareElements) {
124                 var computedStyle2 = window.getComputedStyle(document.getElementById(elementId2)).getPropertyCSSValue(property);
125                 computedValue2 = computedStyle2.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
126                 pass = isCloseEnough(computedValue, computedValue2, tolerance);
127             }
128             else
129                 pass = isCloseEnough(computedValue, expectedValue, tolerance);
130         }
131
132         if (compareElements) {
133             if (pass)
134                 result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + "\" elements at " + time + "s are close enough to each other" + "<br>";
135             else
136                 result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 + "\" which are not close enough to each other" + "<br>";
137         } else {
138             if (pass)
139                 result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>";
140             else
141                 result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
142         }
143     }
144
145     function endTest()
146     {
147         document.getElementById('result').innerHTML = result;
148
149         if (window.layoutTestController)
150             layoutTestController.notifyDone();
151     }
152
153     function checkExpectedValueCallback(expected, index)
154     {
155         return function() { checkExpectedValue(expected, index); };
156     }
157
158     var testStarted = false;
159     function startTest(expected, callback)
160     {
161         if (testStarted) return;
162         testStarted = true;
163
164         if (callback)
165             callback();
166
167         var maxTime = 0;
168
169         for (var i = 0; i < expected.length; ++i) {
170             var animationName = expected[i][0];
171             var time = expected[i][1];
172
173             // We can only use the animation fast-forward mechanism if there's an animation name
174             // and DRT implements pauseAnimationAtTimeOnElementWithId()
175             if (animationName && hasPauseAnimationAPI)
176                 checkExpectedValue(expected, i);
177             else {
178                 if (time > maxTime)
179                     maxTime = time;
180
181                 window.setTimeout(checkExpectedValueCallback(expected, i), time * 1000);
182             }
183         }
184
185         if (maxTime > 0)
186             window.setTimeout(endTest, maxTime * 1000 + 50);
187         else
188             endTest();
189     }
190     
191     if (window.layoutTestController) {
192         layoutTestController.dumpAsText();
193         layoutTestController.waitUntilDone();
194     }
195     
196     if (!expected)
197         throw("Expected results are missing!");
198     
199     var target = document;
200     if (event == undefined)
201         event = "webkitAnimationStart";
202     else if (event == "load")
203         target = window;
204     target.addEventListener(event, function() { startTest(expected, callback); }, false);
205 }