| - meta: | |
| cases = [ |
| ("zero", "0", 0), |
| ("empty", "", None), |
| ("onlyspace", " ", None), |
| ("space", " 100", 100), |
| ("whitespace", "\r\n\t\f100", 100), |
| ("plus", "+100", 100), |
| ("minus", "-100", None), |
| ("octal", "0100", 100), |
| ("hex", "0x100", 0), |
| ("exp", "100e1", 100), |
| ("decimal", "100.999", 100), |
| ("percent", "100%", 100), |
| ("em", "100em", 100), |
| ("junk", "#!?", None), |
| ("trailingjunk", "100#!?", 100), |
| ] |
| def gen(name, string, exp, code): |
| testing = ["size.nonnegativeinteger"] |
| if exp is None: |
| testing.append("size.error") |
| code += "@assert canvas.width === 300;\n@assert canvas.height === 150;\n" |
| expected = "size 300 150" |
| else: |
| code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp) |
| expected = "size %s %s" % (exp, exp) |
| |
| # With "100%", Opera gets canvas.width = 100 but renders at 100% of the frame width, |
| # so check the CSS display width |
| code += '@assert window.getComputedStyle(canvas, null).getPropertyValue("width") === "%spx";\n' % (exp, ) |
| |
| code += "@assert canvas.getAttribute('width') === %r;\n" % string |
| code += "@assert canvas.getAttribute('height') === %r;\n" % string |
| |
| if exp == 0: |
| expected = None # can't generate zero-sized PNGs for the expected image |
| |
| return code, testing, expected |
| |
| for name, string, exp in cases: |
| code = "" |
| code, testing, expected = gen(name, string, exp, code) |
| # We need to replace \r with 
 because \r\n gets converted to \n in the HTML parser. |
| htmlString = string.replace('\r', '
') |
| tests.append( { |
| "name": "size.attributes.parse.%s" % name, |
| "desc": "Parsing of non-negative integers", |
| "testing": testing, |
| "canvas": 'width="%s" height="%s"' % (htmlString, htmlString), |
| "code": code, |
| "expected": expected |
| } ) |
| |
| for name, string, exp in cases: |
| code = "canvas.setAttribute('width', %r);\ncanvas.setAttribute('height', %r);\n" % (string, string) |
| code, testing, expected = gen(name, string, exp, code) |
| tests.append( { |
| "name": "size.attributes.setAttribute.%s" % name, |
| "desc": "Parsing of non-negative integers in setAttribute", |
| "testing": testing, |
| "canvas": 'width="50" height="50"', |
| "code": code, |
| "expected": expected |
| } ) |
| |
| - meta: | |
| state = [ # some non-default values to test with |
| ('strokeStyle', '"#ff0000"'), |
| ('fillStyle', '"#ff0000"'), |
| ('globalAlpha', 0.5), |
| ('lineWidth', 0.5), |
| ('lineCap', '"round"'), |
| ('lineJoin', '"round"'), |
| ('miterLimit', 0.5), |
| ('shadowOffsetX', 5), |
| ('shadowOffsetY', 5), |
| ('shadowBlur', 5), |
| ('shadowColor', '"#ff0000"'), |
| ('globalCompositeOperation', '"copy"'), |
| ('font', '"25px serif"'), |
| ('textAlign', '"center"'), |
| ('textBaseline', '"bottom"'), |
| ] |
| for key,value in state: |
| tests.append( { |
| 'name': '2d.state.saverestore.%s' % key, |
| 'desc': 'save()/restore() works for %s' % key, |
| 'testing': [ '2d.state.%s' % key ], |
| 'code': |
| """// Test that restore() undoes any modifications |
| var old = ctx.%(key)s; |
| ctx.save(); |
| ctx.%(key)s = %(value)s; |
| ctx.restore(); |
| @assert ctx.%(key)s === old; |
| |
| // Also test that save() doesn't modify the values |
| ctx.%(key)s = %(value)s; |
| old = ctx.%(key)s; |
| // we're not interested in failures caused by get(set(x)) != x (e.g. |
| // from rounding), so compare against 'old' instead of against %(value)s |
| ctx.save(); |
| @assert ctx.%(key)s === old; |
| ctx.restore(); |
| """ % { 'key':key, 'value':value } |
| } ) |
| |
| tests.append( { |
| 'name': 'initial.reset.2dstate', |
| 'desc': 'Resetting the canvas state resets 2D state variables', |
| 'testing': [ 'initial.reset' ], |
| 'code': |
| """canvas.width = 100; |
| var default_val; |
| """ + "".join( |
| """ |
| default_val = ctx.%(key)s; |
| ctx.%(key)s = %(value)s; |
| canvas.width = 100; |
| @assert ctx.%(key)s === default_val; |
| """ % { 'key':key, 'value':value } |
| for key,value in state), |
| } ) |
| |
| - meta: | |
| # Composite operation tests |
| # <http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2007-March/010608.html> |
| ops = [ |
| # name FA FB |
| ('source-over', '1', '1-aA'), |
| ('destination-over', '1-aB', '1'), |
| ('source-in', 'aB', '0'), |
| ('destination-in', '0', 'aA'), |
| ('source-out', '1-aB', '0'), |
| ('destination-out', '0', '1-aA'), |
| ('source-atop', 'aB', '1-aA'), |
| ('destination-atop', '1-aB', 'aA'), |
| ('xor', '1-aB', '1-aA'), |
| ('copy', '1', '0'), |
| ('lighter', '1', '1'), |
| ] |
| |
| # The ones that change the output when src = (0,0,0,0): |
| ops_trans = [ 'source-in', 'destination-in', 'source-out', 'destination-atop', 'copy' ]; |
| |
| def calc_output(A, B, FA_code, FB_code): |
| (RA, GA, BA, aA) = A |
| (RB, GB, BB, aB) = B |
| rA, gA, bA = RA*aA, GA*aA, BA*aA |
| rB, gB, bB = RB*aB, GB*aB, BB*aB |
| |
| FA = eval(FA_code) |
| FB = eval(FB_code) |
| |
| rO = rA*FA + rB*FB |
| gO = gA*FA + gB*FB |
| bO = bA*FA + bB*FB |
| aO = aA*FA + aB*FB |
| |
| rO = min(255, rO) |
| gO = min(255, gO) |
| bO = min(255, bO) |
| aO = min(1, aO) |
| |
| if aO: |
| RO = rO / aO |
| GO = gO / aO |
| BO = bO / aO |
| else: RO = GO = BO = 0 |
| |
| return (RO, GO, BO, aO) |
| |
| def to_test(color): |
| r, g, b, a = color |
| return '%d,%d,%d,%d' % (round(r), round(g), round(b), round(a*255)) |
| def to_cairo(color): |
| r, g, b, a = color |
| return '%f,%f,%f,%f' % (r/255., g/255., b/255., a) |
| |
| for (name, src, dest) in [ |
| ('solid', (255, 255, 0, 1.0), (0, 255, 255, 1.0)), |
| ('transparent', (0, 0, 255, 0.75), (0, 255, 0, 0.5)), |
| # catches the atop, xor and lighter bugs in Opera 9.10 |
| ]: |
| for op, FA_code, FB_code in ops: |
| expected = calc_output(src, dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'testing': [ '2d.composite.%s' % op ], |
| 'code': """ |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, src, to_test(expected)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % to_cairo(expected), |
| } ) |
| |
| for (name, src, dest) in [ ('image', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]: |
| for op, FA_code, FB_code in ops: |
| expected = calc_output(src, dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'testing': [ '2d.composite.%s' % op ], |
| 'images': [ 'yellow75.png' ], |
| 'code': """ |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.drawImage(document.getElementById('yellow75.png'), 0, 0); |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, to_test(expected)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % to_cairo(expected), |
| } ) |
| |
| for (name, src, dest) in [ ('canvas', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]: |
| for op, FA_code, FB_code in ops: |
| expected = calc_output(src, dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'testing': [ '2d.composite.%s' % op ], |
| 'images': [ 'yellow75.png' ], |
| 'code': """ |
| var canvas2 = document.createElement('canvas'); |
| canvas2.width = canvas.width; |
| canvas2.height = canvas.height; |
| var ctx2 = canvas2.getContext('2d'); |
| ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0); |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.drawImage(canvas2, 0, 0); |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, to_test(expected)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % to_cairo(expected), |
| } ) |
| |
| |
| for (name, src, dest) in [ ('uncovered.fill', (0, 0, 255, 0.75), (0, 255, 0, 0.5)) ]: |
| for op, FA_code, FB_code in ops: |
| if op not in ops_trans: continue |
| expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'desc': 'fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.', |
| 'testing': [ '2d.composite.%s' % op ], |
| 'code': """ |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.fillStyle = 'rgba%s'; |
| ctx.translate(0, 25); |
| ctx.fillRect(0, 50, 100, 50); |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, src, to_test(expected0)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % (to_cairo(expected0)), |
| } ) |
| |
| for (name, src, dest) in [ ('uncovered.image', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]: |
| for op, FA_code, FB_code in ops: |
| if op not in ops_trans: continue |
| expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'desc': 'drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.', |
| 'testing': [ '2d.composite.%s' % op ], |
| 'images': [ 'yellow.png' ], |
| 'code': """ |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10); |
| @assert pixel 15,15 ==~ %s +/- 5; |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, to_test(expected0), to_test(expected0)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % (to_cairo(expected0)), |
| } ) |
| |
| for (name, src, dest) in [ ('uncovered.nocontext', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]: |
| for op, FA_code, FB_code in ops: |
| if op not in ops_trans: continue |
| expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'desc': 'drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.', |
| 'testing': [ '2d.composite.%s' % op ], |
| 'code': """ |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| var canvas2 = document.createElement('canvas'); |
| ctx.drawImage(canvas2, 0, 0); |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, to_test(expected0)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % (to_cairo(expected0)), |
| } ) |
| |
| for (name, src, dest) in [ ('uncovered.pattern', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]: |
| for op, FA_code, FB_code in ops: |
| if op not in ops_trans: continue |
| expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code) |
| tests.append( { |
| 'name': '2d.composite.%s.%s' % (name, op), |
| 'desc': 'Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.', |
| 'testing': [ '2d.composite.%s' % op ], |
| 'images': [ 'yellow.png' ], |
| 'code': """ |
| ctx.fillStyle = 'rgba%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat'); |
| ctx.fillRect(0, 50, 100, 50); |
| @assert pixel 50,25 ==~ %s +/- 5; |
| """ % (dest, op, to_test(expected0)), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%s) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % (to_cairo(expected0)), |
| } ) |
| |
| for op, FA_code, FB_code in ops: |
| tests.append( { |
| 'name': '2d.composite.clip.%s' % (op), |
| 'desc': 'fill() does not affect pixels outside the clip region.', |
| 'testing': [ '2d.composite.%s' % op ], |
| 'code': """ |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.globalCompositeOperation = '%s'; |
| ctx.rect(-20, -20, 10, 10); |
| ctx.clip(); |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 50, 50); |
| @assert pixel 25,25 == 0,255,0,255; |
| @assert pixel 75,25 == 0,255,0,255; |
| """ % (op), |
| 'expected': 'green' |
| } ) |
| |
| - meta: | |
| # Colour parsing tests |
| |
| # Try most of the CSS3 Color <color> values - http://www.w3.org/TR/css3-color/#colorunits |
| big_float = '1' + ('0' * 39) |
| big_double = '1' + ('0' * 310) |
| for name, string, r,g,b,a, notes in [ |
| ('html4', 'limE', 0,255,0,255, ""), |
| ('hex3', '#0f0', 0,255,0,255, ""), |
| ('hex4', '#0f0f', 0,255,0,255, ""), |
| ('hex6', '#00fF00', 0,255,0,255, ""), |
| ('hex8', '#00ff00ff', 0,255,0,255, ""), |
| ('rgb-num', 'rgb(0,255,0)', 0,255,0,255, ""), |
| ('rgb-clamp-1', 'rgb(-1000, 1000, -1000)', 0,255,0,255, 'Assumes colours are clamped to [0,255].'), |
| ('rgb-clamp-2', 'rgb(-200%, 200%, -200%)', 0,255,0,255, 'Assumes colours are clamped to [0,255].'), |
| ('rgb-clamp-3', 'rgb(-2147483649, 4294967298, -18446744073709551619)', 0,255,0,255, 'Assumes colours are clamped to [0,255].'), |
| ('rgb-clamp-4', 'rgb(-'+big_float+', '+big_float+', -'+big_float+')', 0,255,0,255, 'Assumes colours are clamped to [0,255].'), |
| ('rgb-clamp-5', 'rgb(-'+big_double+', '+big_double+', -'+big_double+')', 0,255,0,255, 'Assumes colours are clamped to [0,255].'), |
| ('rgb-percent', 'rgb(0% ,100% ,0%)', 0,255,0,255, 'CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)'), |
| ('rgb-eof', 'rgb(0, 255, 0', 0,255,0,255, ""), # see CSS2.1 4.2 "Unexpected end of style sheet" |
| ('rgba-solid-1', 'rgba( 0 , 255 , 0 , 1 )', 0,255,0,255, ""), |
| ('rgba-solid-2', 'rgba( 0 , 255 , 0 , 1.0 )', 0,255,0,255, ""), |
| ('rgba-solid-3', 'rgba( 0 , 255 , 0 , +1 )', 0,255,0,255, ""), |
| ('rgba-solid-4', 'rgba( -0 , 255 , +0 , 1 )', 0,255,0,255, ""), |
| ('rgba-num-1', 'rgba( 0 , 255 , 0 , .499 )', 0,255,0,127, ""), |
| ('rgba-num-2', 'rgba( 0 , 255 , 0 , 0.499 )', 0,255,0,127, ""), |
| ('rgba-percent', 'rgba(0%,100%,0%,0.499)', 0,255,0,127, ""), # 0.499*255 rounds to 127, both down and nearest, so it should be safe |
| ('rgba-clamp-1', 'rgba(0, 255, 0, -2)', 0,0,0,0, ""), |
| ('rgba-clamp-2', 'rgba(0, 255, 0, 2)', 0,255,0,255, ""), |
| ('rgba-eof', 'rgba(0, 255, 0, 1', 0,255,0,255, ""), |
| ('transparent-1', 'transparent', 0,0,0,0, ""), |
| ('transparent-2', 'TrAnSpArEnT', 0,0,0,0, ""), |
| ('hsl-1', 'hsl(120, 100%, 50%)', 0,255,0,255, ""), |
| ('hsl-2', 'hsl( -240 , 100% , 50% )', 0,255,0,255, ""), |
| ('hsl-3', 'hsl(360120, 100%, 50%)', 0,255,0,255, ""), |
| ('hsl-4', 'hsl(-360240, 100%, 50%)', 0,255,0,255, ""), |
| ('hsl-5', 'hsl(120.0, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('hsl-6', 'hsl(+120, +100%, +50%)', 0,255,0,255, ""), |
| ('hsl-clamp-1', 'hsl(120, 200%, 50%)', 0,255,0,255, ""), |
| ('hsl-clamp-2', 'hsl(120, -200%, 49.9%)', 127,127,127,255, ""), |
| ('hsl-clamp-3', 'hsl(120, 100%, 200%)', 255,255,255,255, ""), |
| ('hsl-clamp-4', 'hsl(120, 100%, -200%)', 0,0,0,255, ""), |
| ('hsla-1', 'hsla(120, 100%, 50%, 0.499)', 0,255,0,127, ""), |
| ('hsla-2', 'hsla( 120.0 , 100.0% , 50.0% , 1 )', 0,255,0,255, ""), |
| ('hsla-clamp-1', 'hsla(120, 200%, 50%, 1)', 0,255,0,255, ""), |
| ('hsla-clamp-2', 'hsla(120, -200%, 49.9%, 1)', 127,127,127,255, ""), |
| ('hsla-clamp-3', 'hsla(120, 100%, 200%, 1)', 255,255,255,255, ""), |
| ('hsla-clamp-4', 'hsla(120, 100%, -200%, 1)', 0,0,0,255, ""), |
| ('hsla-clamp-5', 'hsla(120, 100%, 50%, 2)', 0,255,0,255, ""), |
| ('hsla-clamp-6', 'hsla(120, 100%, 0%, -2)', 0,0,0,0, ""), |
| ('svg-1', 'gray', 128,128,128,255, ""), |
| ('svg-2', 'grey', 128,128,128,255, ""), |
| # css-color-4 rgb() color function |
| # https://drafts.csswg.org/css-color/#numeric-rgb |
| ('css-color-4-rgb-1', 'rgb(0, 255.0, 0)', 0,255,0,255, ""), |
| ('css-color-4-rgb-2', 'rgb(0, 255, 0, 0.2)', 0,255,0,51, ""), |
| ('css-color-4-rgb-3', 'rgb(0, 255, 0, 20%)', 0,255,0,51, ""), |
| ('css-color-4-rgb-4', 'rgb(0 255 0)', 0,255,0,255, ""), |
| ('css-color-4-rgb-5', 'rgb(0 255 0 / 0.2)', 0,255,0,51, ""), |
| ('css-color-4-rgb-6', 'rgb(0 255 0 / 20%)', 0,255,0,51, ""), |
| ('css-color-4-rgba-1', 'rgba(0, 255.0, 0)', 0,255,0,255, ""), |
| ('css-color-4-rgba-2', 'rgba(0, 255, 0, 0.2)', 0,255,0,51, ""), |
| ('css-color-4-rgba-3', 'rgba(0, 255, 0, 20%)', 0,255,0,51, ""), |
| ('css-color-4-rgba-4', 'rgba(0 255 0)', 0,255,0,255, ""), |
| ('css-color-4-rgba-5', 'rgba(0 255 0 / 0.2)', 0,255,0,51, ""), |
| ('css-color-4-rgba-6', 'rgba(0 255 0 / 20%)', 0,255,0,51, ""), |
| # css-color-4 hsl() color function |
| # https://drafts.csswg.org/css-color/#the-hsl-notation |
| ('css-color-4-hsl-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsl-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""), |
| ('css-color-4-hsl-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""), |
| ('css-color-4-hsl-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""), |
| ('css-color-4-hsl-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""), |
| ('css-color-4-hsl-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsl-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsl-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsl-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsla-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsla-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""), |
| ('css-color-4-hsla-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""), |
| ('css-color-4-hsla-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""), |
| ('css-color-4-hsla-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""), |
| ('css-color-4-hsla-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsla-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsla-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""), |
| ('css-color-4-hsla-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""), |
| # currentColor is handled later |
| ]: |
| # TODO: test by retrieving fillStyle, instead of actually drawing? |
| # TODO: test strokeStyle, shadowColor in the same way |
| test = { |
| 'name': '2d.fillStyle.parse.%s' % name, |
| 'testing': [ '2d.colours.parse' ], |
| 'notes': notes, |
| 'code': """ |
| ctx.fillStyle = '#f00'; |
| ctx.fillStyle = '%s'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == %d,%d,%d,%d; |
| """ % (string, r,g,b,a), |
| 'expected': """size 100 50 |
| cr.set_source_rgba(%f, %f, %f, %f) |
| cr.rectangle(0, 0, 100, 50) |
| cr.fill() |
| """ % (r/255., g/255., b/255., a/255.), |
| } |
| tests.append(test) |
| |
| # Also test that invalid colours are ignored |
| for name, string in [ |
| ('hex1', '#f'), |
| ('hex2', '#f0'), |
| ('hex3', '#g00'), |
| ('hex4', '#fg00'), |
| ('hex5', '#ff000'), |
| ('hex6', '#fg0000'), |
| ('hex7', '#ff0000f'), |
| ('hex8', '#fg0000ff'), |
| ('rgb-1', 'rgb(255.0, 0, 0,)'), |
| ('rgb-2', 'rgb(100%, 0, 0)'), |
| ('rgb-3', 'rgb(255, - 1, 0)'), |
| ('rgba-1', 'rgba(100%, 0, 0, 1)'), |
| ('rgba-2', 'rgba(255, 0, 0, 1. 0)'), |
| ('rgba-3', 'rgba(255, 0, 0, 1.)'), |
| ('rgba-4', 'rgba(255, 0, 0, '), |
| ('rgba-5', 'rgba(255, 0, 0, 1,)'), |
| ('hsl-1', 'hsl(0%, 100%, 50%)'), |
| ('hsl-2', 'hsl(z, 100%, 50%)'), |
| ('hsl-3', 'hsl(0, 0, 50%)'), |
| ('hsl-4', 'hsl(0, 100%, 0)'), |
| ('hsl-5', 'hsl(0, 100.%, 50%)'), |
| ('hsl-6', 'hsl(0, 100%, 50%,)'), |
| ('hsla-1', 'hsla(0%, 100%, 50%, 1)'), |
| ('hsla-2', 'hsla(0, 0, 50%, 1)'), |
| ('hsla-3', 'hsla(0, 0, 50%, 1,)'), |
| ('name-1', 'darkbrown'), |
| ('name-2', 'firebrick1'), |
| ('name-3', 'red blue'), |
| ('name-4', '"red"'), |
| ('name-5', '"red'), |
| # css-color-4 color function |
| # comma and comma-less expressions should not mix together. |
| ('css-color-4-rgb-1', 'rgb(255, 0, 0 / 1)'), |
| ('css-color-4-rgb-2', 'rgb(255 0 0, 1)'), |
| ('css-color-4-rgb-3', 'rgb(255, 0 0)'), |
| ('css-color-4-rgba-1', 'rgba(255, 0, 0 / 1)'), |
| ('css-color-4-rgba-2', 'rgba(255 0 0, 1)'), |
| ('css-color-4-rgba-3', 'rgba(255, 0 0)'), |
| ('css-color-4-hsl-1', 'hsl(0, 100%, 50% / 1)'), |
| ('css-color-4-hsl-2', 'hsl(0 100% 50%, 1)'), |
| ('css-color-4-hsl-3', 'hsl(0, 100% 50%)'), |
| ('css-color-4-hsla-1', 'hsla(0, 100%, 50% / 1)'), |
| ('css-color-4-hsla-2', 'hsla(0 100% 50%, 1)'), |
| ('css-color-4-hsla-3', 'hsla(0, 100% 50%)'), |
| # trailing slash |
| ('css-color-4-rgb-4', 'rgb(0 0 0 /)'), |
| ('css-color-4-rgb-5', 'rgb(0, 0, 0 /)'), |
| ('css-color-4-hsl-4', 'hsl(0 100% 50% /)'), |
| ('css-color-4-hsl-5', 'hsl(0, 100%, 50% /)'), |
| ]: |
| test = { |
| 'name': '2d.fillStyle.parse.invalid.%s' % name, |
| 'testing': [ '2d.colours.parse' ], |
| 'code': """ |
| ctx.fillStyle = '#0f0'; |
| try { ctx.fillStyle = '%s'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| """ % string, |
| 'expected': 'green' |
| } |
| tests.append(test) |
| |
| # Some can't have positive tests, only negative tests, because we don't know what colour they're meant to be |
| for name, string in [ |
| ('system', 'ThreeDDarkShadow'), |
| #('flavor', 'flavor'), # removed from latest CSS3 Color drafts |
| ]: |
| test = { |
| 'name': '2d.fillStyle.parse.%s' % name, |
| 'testing': [ '2d.colours.parse' ], |
| 'code': """ |
| ctx.fillStyle = '#f00'; |
| ctx.fillStyle = '%s'; |
| @assert ctx.fillStyle =~ /^#(?!(FF0000|ff0000|f00)$)/; // test that it's not red |
| """ % (string,), |
| } |
| tests.append(test) |
| |