summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml')
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml539
1 files changed, 539 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml b/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml
new file mode 100644
index 0000000000..7b44fd9f26
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml
@@ -0,0 +1,539 @@
+- 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"'),
+ ]
+ for key,value in state:
+ tests.append( {
+ 'name': '2d.state.saverestore.%s' % key,
+ 'desc': 'save()/restore() works for %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();
+ t.done();
+ """ % { 'key':key, 'value':value }
+ } )
+
+ tests.append( {
+ 'name': '2d.canvas.host.initial.reset.2dstate',
+ 'desc': 'Resetting the canvas state resets 2D state variables',
+ '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) + "\nt.done();",
+ } )
+
+- 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'),
+ ('clear', '0', '0'),
+ ]
+
+ # 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),
+ '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;
+ t.done();
+ """ % (dest, op, src, to_test(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),
+ 'images': [ 'yellow75.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(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),
+ 'images': [ 'yellow75.png' ],
+ 'code': """
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(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)
+ new_test = {
+ '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.',
+ '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;
+ t.done();
+ """ % (dest, op, src, to_test(expected0)),
+ }
+ if op == 'destination-in':
+ new_test['timeout'] = 'long'
+ tests.append(new_test)
+
+ 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.',
+ 'images': [ 'yellow.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ @assert pixel 15,15 ==~ %s +/- 5;
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(expected0), to_test(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.',
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ t.done();
+ """ % (dest, op, to_test(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.',
+ 'images': [ 'yellow.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(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.',
+ '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;
+ t.done();
+ """ % (op),
+ } )
+
+- meta: |
+ # Color 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 colors are clamped to [0,255].'),
+ ('rgb-clamp-2', 'rgb(-200%, 200%, -200%)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-3', 'rgb(-2147483649, 4294967298, -18446744073709551619)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-4', 'rgb(-'+big_float+', '+big_float+', -'+big_float+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-5', 'rgb(-'+big_double+', '+big_double+', -'+big_double+')', 0,255,0,255, 'Assumes colors 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,
+ 'notes': notes,
+ 'code': """
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '%s';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == %d,%d,%d,%d;
+ t.done();
+ """ % (string, r,g,b,a),
+ }
+ tests.append(test)
+
+ # Also test that invalid colors 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,
+ '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;
+ t.done();
+ """ % string,
+ }
+ tests.append(test)
+
+ # Some can't have positive tests, only negative tests, because we don't know what color 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,
+ 'code': """
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '%s';
+ @assert ctx.fillStyle =~ /^#(?!(FF0000|ff0000|f00)$)/; // test that it's not red
+ t.done();
+ """ % (string,),
+ }
+ tests.append(test)
+
+- meta: |
+ cases = [
+ ("zero", "0", 0),
+ ("empty", "", 0),
+ ("onlyspace", " ", 0),
+ ("space", " 100", 100),
+ ("whitespace", "\t\f100", 100),
+ ("plus", "+100", 100),
+ ("minus", "-100", "exception"),
+ ("octal", "0100", 100),
+ ("hex", "0x100", 0x100),
+ ("exp", "100e1", 100e1),
+ ("decimal", "100.999", 100),
+ ("percent", "100%", "exception"),
+ ("em", "100em", "exception"),
+ ("junk", "#!?", "exception"),
+ ("trailingjunk", "100#!?", "exception"),
+ ]
+ def gen(name, string, exp, code):
+ if exp is None:
+ code += "canvas.width = '%s';\ncanvas.height = '%s';\n" % (string, string)
+ code += "@assert canvas.width === 100;\n@assert canvas.height === 50;\n"
+ expected = None
+ elif exp == "exception":
+ code += "@assert throws TypeError canvas.width = '%s';\n" % string
+ expected = None
+ else:
+ code += "canvas.width = '%s';\ncanvas.height = '%s';\n" % (string, string)
+ code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp)
+ expected = None
+
+ code += "t.done();\n"
+
+ if exp == 0:
+ expected = None # can't generate zero-sized PNGs for the expected image
+
+ return code, expected
+
+ for name, string, exp in cases:
+ code = ""
+ code, expected = gen(name, string, exp, code)
+ tests.append( {
+ "name": "2d.canvas.host.size.attributes.parse.%s" % name,
+ "desc": "Parsing of non-negative integers",
+ "code": code,
+ } )