 Chromium Code Reviews
 Chromium Code Reviews Issue 15738009:
  Beat the transition tests with a sanity stick.  (Closed) 
  Base URL: svn://svn.chromium.org/blink/trunk
    
  
    Issue 15738009:
  Beat the transition tests with a sanity stick.  (Closed) 
  Base URL: svn://svn.chromium.org/blink/trunk| OLD | NEW | 
|---|---|
| 1 /* This is the helper function to run animation tests: | 1 /* This is the helper function to run animation tests: | 
| 2 | 2 | 
| 3 Test page requirements: | 3 Test page requirements: | 
| 4 - The body must contain an empty div with id "result" | 4 - The body must contain an empty div with id "result" | 
| 5 - Call this function directly from the <script> inside the test page | 5 - Call this function directly from the <script> inside the test page | 
| 6 | 6 | 
| 7 Function parameters: | 7 Function parameters: | 
| 8 expected [required]: an array of arrays defining a set of CSS properties tha t must have given values at specific times (see below) | 8 expected [required]: an array of arrays defining a set of CSS properties tha t must have given values at specific times (see below) | 
| 9 callbacks [optional]: a function to be executed immediately after animation starts; | 9 callbacks [optional]: a function to be executed immediately after animation starts; | 
| 10 or, an object in the form {time: function} containing functions to be | 10 or, an object in the form {time: function} containing functions to be | 
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 If the CSS property name is "webkitTransform.N", expected value must be a nu mber corresponding to the Nth element of the matrix | 33 If the CSS property name is "webkitTransform.N", expected value must be a nu mber corresponding to the Nth element of the matrix | 
| 34 | 34 | 
| 35 */ | 35 */ | 
| 36 | 36 | 
| 37 function isCloseEnough(actual, desired, tolerance) | 37 function isCloseEnough(actual, desired, tolerance) | 
| 38 { | 38 { | 
| 39 var diff = Math.abs(actual - desired); | 39 var diff = Math.abs(actual - desired); | 
| 40 return diff <= tolerance; | 40 return diff <= tolerance; | 
| 41 } | 41 } | 
| 42 | 42 | 
| 43 function roundNumber(num, decimalPlaces) | |
| 44 { | |
| 45 return Math.round(num * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPla ces); | |
| 46 } | |
| 47 | |
| 43 function matrixStringToArray(s) | 48 function matrixStringToArray(s) | 
| 44 { | 49 { | 
| 45 if (s == "none") | 50 if (s == "none") | 
| 46 return [ 1, 0, 0, 1, 0, 0 ]; | 51 return [ 1, 0, 0, 1, 0, 0 ]; | 
| 47 var m = s.split("("); | 52 var m = s.split("("); | 
| 48 m = m[1].split(")"); | 53 m = m[1].split(")"); | 
| 49 return m[0].split(","); | 54 return m[0].split(","); | 
| 50 } | 55 } | 
| 51 | 56 | 
| 52 function parseCrossFade(s) | 57 function parseCrossFade(s) | 
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 | 268 | 
| 264 if (comparePropertyValue(property, computedValue, expectedValue, toleran ce)) | 269 if (comparePropertyValue(property, computedValue, expectedValue, toleran ce)) | 
| 265 result += "PASS - \"" + property + "\" property for \"" + elementNam e + "\" element at " + time + | 270 result += "PASS - \"" + property + "\" property for \"" + elementNam e + "\" element at " + time + | 
| 266 "s saw something close to: " + expectedValue + "<br> "; | 271 "s saw something close to: " + expectedValue + "<br> "; | 
| 267 else | 272 else | 
| 268 result += "FAIL - \"" + property + "\" property for \"" + elementNam e + "\" element at " + time + | 273 result += "FAIL - \"" + property + "\" property for \"" + elementNam e + "\" element at " + time + | 
| 269 "s expected: " + expectedValue + " but saw: " + comp utedValue + "<br>"; | 274 "s expected: " + expectedValue + " but saw: " + comp utedValue + "<br>"; | 
| 270 } | 275 } | 
| 271 } | 276 } | 
| 272 | 277 | 
| 278 function compareRGB(rgb, expected, tolerance) | |
| 279 { | |
| 280 return (isCloseEnough(parseInt(rgb[0]), expected[0], tolerance) && | |
| 281 isCloseEnough(parseInt(rgb[1]), expected[1], tolerance) && | |
| 282 isCloseEnough(parseInt(rgb[2]), expected[2], tolerance)); | |
| 283 } | |
| 284 | |
| 285 function parseCrossFade(s) | |
| 286 { | |
| 287 var matches = s.match("-webkit-cross-fade\\((.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\ )"); | |
| 288 | |
| 289 if (!matches) | |
| 290 return null; | |
| 291 | |
| 292 return {"from": matches[1], "to": matches[2], "percent": parseFloat(matches[ 3])} | |
| 293 } | |
| 294 | |
| 295 function checkExpectedTransitionValue(expected, index) | |
| 
Steve Block
2013/05/23 03:47:09
Have you changed this function at all from transit
 
dstockwell
2013/05/23 03:59:33
Only some trivial changes, inlining some simple fu
 | |
| 296 { | |
| 297 expected[index].shift(); | |
| 298 var time = expected[index][0]; | |
| 299 var elementId = expected[index][1]; | |
| 300 var property = expected[index][2]; | |
| 301 var expectedValue = expected[index][3]; | |
| 302 var tolerance = expected[index][4]; | |
| 303 var postCompletionCallback = expected[index][5]; | |
| 304 | |
| 305 var computedValue; | |
| 306 var pass = false; | |
| 307 var transformRegExp = /^-webkit-transform(\.\d+)?$/; | |
| 308 if (transformRegExp.test(property)) { | |
| 309 computedValue = window.getComputedStyle(document.getElementById(elementI d)).webkitTransform; | |
| 310 if (typeof expectedValue == "string") | |
| 311 pass = (computedValue == expectedValue); | |
| 312 else if (typeof expectedValue == "number") { | |
| 313 var m = computedValue.split("("); | |
| 314 var m = m[1].split(","); | |
| 315 pass = isCloseEnough(parseFloat(m[parseInt(property.substring(18))]) , expectedValue, tolerance); | |
| 316 } else { | |
| 317 var m = computedValue.split("("); | |
| 318 var m = m[1].split(","); | |
| 319 for (i = 0; i < expectedValue.length; ++i) { | |
| 320 pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], toleran ce); | |
| 321 if (!pass) | |
| 322 break; | |
| 323 } | |
| 324 } | |
| 325 } else if (property == "fill" || property == "stroke") { | |
| 326 computedValue = window.getComputedStyle(document.getElementById(elementI d)).getPropertyCSSValue(property).rgbColor; | |
| 327 if (compareRGB([computedValue.red.cssText, computedValue.green.cssText, computedValue.blue.cssText], expectedValue, tolerance)) | |
| 328 pass = true; | |
| 329 else { | |
| 330 // We failed. Make sure computed value is something we can read in t he error message | |
| 331 computedValue = window.getComputedStyle(document.getElementById(elem entId)).getPropertyCSSValue(property).cssText; | |
| 332 } | |
| 333 } else if (property == "stop-color" || property == "flood-color" || property == "lighting-color") { | |
| 334 computedValue = window.getComputedStyle(document.getElementById(elementI d)).getPropertyCSSValue(property); | |
| 335 // The computedValue cssText is rgb(num, num, num) | |
| 336 var components = computedValue.cssText.split("(")[1].split(")")[0].split (","); | |
| 337 if (compareRGB(components, expectedValue, tolerance)) | |
| 338 pass = true; | |
| 339 else { | |
| 340 // We failed. Make sure computed value is something we can read in t he error message | |
| 341 computedValue = computedValue.cssText; | |
| 342 } | |
| 343 } else if (property == "lineHeight") { | |
| 344 computedValue = parseInt(window.getComputedStyle(document.getElementById (elementId)).lineHeight); | |
| 345 pass = isCloseEnough(computedValue, expectedValue, tolerance); | |
| 346 } else if (property == "background-image" | |
| 347 || property == "border-image-source" | |
| 348 || property == "border-image" | |
| 349 || property == "list-style-image" | |
| 350 || property == "-webkit-mask-image" | |
| 351 || property == "-webkit-mask-box-image") { | |
| 352 if (property == "border-image" || property == "-webkit-mask-image" || pr operty == "-webkit-mask-box-image") | |
| 353 property += "-source"; | |
| 354 | |
| 355 computedValue = window.getComputedStyle(document.getElementById(elementI d)).getPropertyCSSValue(property).cssText; | |
| 356 computedCrossFade = parseCrossFade(computedValue); | |
| 357 | |
| 358 if (!computedCrossFade) { | |
| 359 pass = false; | |
| 360 } else { | |
| 361 pass = isCloseEnough(computedCrossFade.percent, expectedValue, toler ance); | |
| 362 } | |
| 363 } else { | |
| 364 var computedStyle = window.getComputedStyle(document.getElementById(elem entId)).getPropertyCSSValue(property); | |
| 365 if (computedStyle.cssValueType == CSSValue.CSS_VALUE_LIST) { | |
| 366 var values = []; | |
| 367 for (var i = 0; i < computedStyle.length; ++i) { | |
| 368 switch (computedStyle[i].cssValueType) { | |
| 369 case CSSValue.CSS_PRIMITIVE_VALUE: | |
| 370 values.push(computedStyle[i].getFloatValue(CSSPrimitiveValue .CSS_NUMBER)); | |
| 371 break; | |
| 372 case CSSValue.CSS_CUSTOM: | |
| 373 // arbitrarily pick shadow-x and shadow-y | |
| 374 if (property == 'box-shadow' || property == 'text-shadow') { | |
| 375 var text = computedStyle[i].cssText; | |
| 376 // Shadow cssText looks like "rgb(0, 0, 255) 0px -3px 10px 0px" | |
| 377 var shadowPositionRegExp = /\)\s*(-?\d+)px\s*(-?\d+)px/; | |
| 378 var match = shadowPositionRegExp.exec(text); | |
| 379 var shadowXY = [parseInt(match[1]), parseInt(match[2])]; | |
| 380 values.push(shadowXY[0]); | |
| 381 values.push(shadowXY[1]); | |
| 382 } else | |
| 383 values.push(computedStyle[i].cssText); | |
| 384 break; | |
| 385 } | |
| 386 } | |
| 387 computedValue = values.join(','); | |
| 388 pass = true; | |
| 389 for (var i = 0; i < values.length; ++i) | |
| 390 pass &= isCloseEnough(values[i], expectedValue[i], tolerance); | |
| 391 } else if (computedStyle.cssValueType == CSSValue.CSS_PRIMITIVE_VALUE) { | |
| 392 switch (computedStyle.primitiveType) { | |
| 393 case CSSPrimitiveValue.CSS_STRING: | |
| 394 case CSSPrimitiveValue.CSS_IDENT: | |
| 395 computedValue = computedStyle.getStringValue(); | |
| 396 pass = computedValue == expectedValue; | |
| 397 break; | |
| 398 case CSSPrimitiveValue.CSS_RGBCOLOR: | |
| 399 var rgbColor = computedStyle.getRGBColorValue(); | |
| 400 computedValue = [rgbColor.red.getFloatValue(CSSPrimitiveValu e.CSS_NUMBER), | |
| 401 rgbColor.green.getFloatValue(CSSPrimitiveVa lue.CSS_NUMBER), | |
| 402 rgbColor.blue.getFloatValue(CSSPrimitiveVal ue.CSS_NUMBER)]; // alpha is not exposed to JS | |
| 403 pass = true; | |
| 404 for (var i = 0; i < 3; ++i) | |
| 405 pass &= isCloseEnough(computedValue[i], expectedValue[i] , tolerance); | |
| 406 break; | |
| 407 case CSSPrimitiveValue.CSS_RECT: | |
| 408 computedValue = computedStyle.getRectValue(); | |
| 409 computedValue = [computedValue.top.getFloatValue(CSSPrimitiv eValue.CSS_NUMBER), | |
| 410 computedValue.right.getFloatValue(CSSPrimit iveValue.CSS_NUMBER), | |
| 411 computedValue.bottom.getFloatValue(CSSPrimi tiveValue.CSS_NUMBER), | |
| 412 computedValue.left.getFloatValue(CSSPrimiti veValue.CSS_NUMBER)]; | |
| 413 pass = true; | |
| 414 for (var i = 0; i < 4; ++i) | |
| 415 pass &= isCloseEnough(computedValue[i], expectedValue[i ], tolerance); | |
| 416 break; | |
| 417 case CSSPrimitiveValue.CSS_PERCENTAGE: | |
| 418 computedValue = parseFloat(computedStyle.cssText); | |
| 419 pass = isCloseEnough(computedValue, expectedValue, tolerance ); | |
| 420 break; | |
| 421 default: | |
| 422 computedValue = computedStyle.getFloatValue(CSSPrimitiveValu e.CSS_NUMBER); | |
| 423 pass = isCloseEnough(computedValue, expectedValue, tolerance ); | |
| 424 } | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 if (pass) | |
| 429 result += "PASS - \"" + property + "\" property for \"" + elementId + "\ " element at " + time + "s saw something close to: " + expectedValue + "<br>"; | |
| 430 else | |
| 431 result += "FAIL - \"" + property + "\" property for \"" + elementId + "\ " element at " + time + "s expected: " + expectedValue + " but saw: " + computed Value + "<br>"; | |
| 432 | |
| 433 if (postCompletionCallback) | |
| 434 result += postCompletionCallback(); | |
| 435 } | |
| 436 | |
| 273 | 437 | 
| 274 function getPropertyValue(property, elementId, iframeId) | 438 function getPropertyValue(property, elementId, iframeId) | 
| 275 { | 439 { | 
| 276 var computedValue; | 440 var computedValue; | 
| 277 var element; | 441 var element; | 
| 278 if (iframeId) | 442 if (iframeId) | 
| 279 element = document.getElementById(iframeId).contentDocument.getElementBy Id(elementId); | 443 element = document.getElementById(iframeId).contentDocument.getElementBy Id(elementId); | 
| 280 else | 444 else | 
| 281 element = document.getElementById(elementId); | 445 element = document.getElementById(elementId); | 
| 282 | 446 | 
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 395 if (hasPauseAnimationAPI) | 559 if (hasPauseAnimationAPI) | 
| 396 runChecksWithPauseAPI(checks); | 560 runChecksWithPauseAPI(checks); | 
| 397 else | 561 else | 
| 398 runChecksWithRAF(checks); | 562 runChecksWithRAF(checks); | 
| 399 } | 563 } | 
| 400 | 564 | 
| 401 var useResultElement = false; | 565 var useResultElement = false; | 
| 402 var result = ""; | 566 var result = ""; | 
| 403 var hasPauseAnimationAPI; | 567 var hasPauseAnimationAPI; | 
| 404 var animStartTime; | 568 var animStartTime; | 
| 569 var isTransitionsTest = false; | |
| 570 | |
| 571 var usePauseAPI = true; | |
| 572 var dontUsePauseAPI = false; | |
| 573 var shouldBeTransitioning = 'should-be-transitioning'; | |
| 574 var shouldNotBeTransitioning = 'should-not-be-transitioning'; | |
| 405 | 575 | 
| 406 // FIXME: remove deprecatedEvent, disablePauseAnimationAPI and doPixelTest | 576 // FIXME: remove deprecatedEvent, disablePauseAnimationAPI and doPixelTest | 
| 407 function runAnimationTest(expected, callbacks, deprecatedEvent, disablePauseAnim ationAPI, doPixelTest) | 577 function runAnimationTest(expected, callbacks, deprecatedEvent, disablePauseAnim ationAPI, doPixelTest) | 
| 408 { | 578 { | 
| 409 if (disablePauseAnimationAPI) | 579 if (disablePauseAnimationAPI) | 
| 410 result += 'Warning this test is running in real-time and may be flaky.<b r>'; | 580 result += 'Warning this test is running in real-time and may be flaky.<b r>'; | 
| 411 if (deprecatedEvent) | 581 if (deprecatedEvent) | 
| 412 throw 'Event argument is deprecated!'; | 582 throw 'Event argument is deprecated!'; | 
| 413 if (!expected) | 583 if (!expected) | 
| 414 throw "Expected results are missing!"; | 584 throw "Expected results are missing!"; | 
| 415 | 585 | 
| 416 hasPauseAnimationAPI = 'internals' in window; | 586 hasPauseAnimationAPI = 'internals' in window; | 
| 417 if (disablePauseAnimationAPI) | 587 if (disablePauseAnimationAPI) | 
| 418 hasPauseAnimationAPI = false; | 588 hasPauseAnimationAPI = false; | 
| 419 | 589 | 
| 420 var checks = {}; | 590 var checks = {}; | 
| 591 var trigger = function() {}; | |
| 421 | 592 | 
| 422 if (typeof callbacks == 'function') | 593 if (isTransitionsTest) { | 
| 594 var transitionTrigger = callbacks; | |
| 595 callbacks = null; | |
| 596 trigger = function() { | |
| 597 transitionTrigger(); | |
| 598 document.body.offsetTop | |
| 599 if (window.testRunner) | |
| 600 testRunner.display(); | |
| 601 }; | |
| 602 } | |
| 603 | |
| 604 if (typeof callbacks == 'function') { | |
| 423 checks[0] = [callbacks]; | 605 checks[0] = [callbacks]; | 
| 424 else for (var time in callbacks) { | 606 } else for (var time in callbacks) { | 
| 425 timeMs = Math.round(time * 1000); | 607 timeMs = Math.round(time * 1000); | 
| 426 checks[timeMs] = [callbacks[time]]; | 608 checks[timeMs] = [callbacks[time]]; | 
| 427 } | 609 } | 
| 428 | 610 | 
| 429 for (var i = 0; i < expected.length; i++) { | 611 for (var i = 0; i < expected.length; i++) { | 
| 430 var expectation = expected[i]; | 612 var expectation = expected[i]; | 
| 431 var timeMs = Math.round(expectation[1] * 1000); | 613 var timeMs = Math.round(expectation[1] * 1000); | 
| 432 if (!checks[timeMs]) | 614 if (!checks[timeMs]) | 
| 433 checks[timeMs] = []; | 615 checks[timeMs] = []; | 
| 434 checks[timeMs].push(checkExpectedValue.bind(null, expected, i)); | 616 if (isTransitionsTest) | 
| 617 checks[timeMs].push(checkExpectedTransitionValue.bind(null, expected , i)); | |
| 618 else | |
| 619 checks[timeMs].push(checkExpectedValue.bind(null, expected, i)); | |
| 435 } | 620 } | 
| 436 | 621 | 
| 437 var doPixelTest = Boolean(doPixelTest); | 622 var doPixelTest = Boolean(doPixelTest); | 
| 438 useResultElement = doPixelTest; | 623 useResultElement = doPixelTest; | 
| 439 | 624 | 
| 440 if (window.testRunner) { | 625 if (window.testRunner) { | 
| 441 testRunner.dumpAsText(doPixelTest); | 626 testRunner.dumpAsText(doPixelTest); | 
| 442 testRunner.waitUntilDone(); | 627 testRunner.waitUntilDone(); | 
| 443 } | 628 } | 
| 444 | 629 | 
| 445 var started = false; | 630 var started = false; | 
| 446 document.addEventListener('webkitAnimationStart', function() { | 631 var target = isTransitionsTest ? window : document; | 
| 632 var event = isTransitionsTest ? 'load' : 'webkitAnimationStart'; | |
| 633 target.addEventListener(event, function() { | |
| 447 if (!started) { | 634 if (!started) { | 
| 448 started = true; | 635 started = true; | 
| 636 trigger(); | |
| 449 animStartTime = performance.now(); | 637 animStartTime = performance.now(); | 
| 450 // delay to give hardware animations a chance to start | 638 // delay to give hardware animations a chance to start | 
| 451 setTimeout(function() { | 639 setTimeout(function() { | 
| 452 startTest(checks); | 640 startTest(checks); | 
| 453 }, 0); | 641 }, 0); | 
| 454 } | 642 } | 
| 455 }, false); | 643 }, false); | 
| 456 } | 644 } | 
| 645 | |
| 646 /* This is the helper function to run transition tests: | |
| 647 | |
| 648 Test page requirements: | |
| 649 - The body must contain an empty div with id "result" | |
| 650 - Call this function directly from the <script> inside the test page | |
| 651 | |
| 652 Function parameters: | |
| 653 expected [required]: an array of arrays defining a set of CSS properties tha t must have given values at specific times (see below) | |
| 654 callback [optional]: a function to be executed just before the test starts ( none by default) | |
| 655 | |
| 656 Each sub-array must contain these items in this order: | |
| 657 - the time in seconds at which to snapshot the CSS property | |
| 658 - the id of the element on which to get the CSS property value | |
| 659 - the name of the CSS property to get [1] | |
| 660 - the expected value for the CSS property | |
| 661 - the tolerance to use when comparing the effective CSS property value with its expected value | |
| 662 | |
| 663 [1] If the CSS property name is "-webkit-transform", expected value must be an array of 1 or more numbers corresponding to the matrix elements, | |
| 664 or a string which will be compared directly (useful if the expected value is "none") | |
| 665 If the CSS property name is "-webkit-transform.N", expected value must be a number corresponding to the Nth element of the matrix | |
| 666 | |
| 667 */ | |
| 668 function runTransitionTest(expected, callback, usePauseAPI, doPixelTest) { | |
| 669 expected = expected.map(function(expectation) { expectation.unshift(null); r eturn expectation; }); | |
| 670 isTransitionsTest = true; | |
| 671 runAnimationTest(expected, callback, undefined, usePauseAPI !== true, doPixe lTest); | |
| 
Steve Block
2013/05/23 03:47:09
Regarding usePauseAPI, are there really cases wher
 
dstockwell
2013/05/23 03:59:33
Done.
 | |
| 672 } | |
| OLD | NEW |