Bug 1773348 [wpt PR 34351] - Fix 'colour' -> 'color' spelling in WPT Canvas tests...
[gecko.git] / testing / web-platform / tests / html / canvas / tools / yaml / element / meta.yaml
blobf6902d07822b70bb560663aa9aee07f9e073a0b7
1 - meta: |
2     cases = [
3         ("zero", "0", 0),
4         ("empty", "", None),
5         ("onlyspace", "  ", None),
6         ("space", "  100", 100),
7         ("whitespace", "\r\n\t\f100", 100),
8         ("plus", "+100", 100),
9         ("minus", "-100", None),
10         ("octal", "0100", 100),
11         ("hex", "0x100", 0),
12         ("exp", "100e1", 100),
13         ("decimal", "100.999", 100),
14         ("percent", "100%", 100),
15         ("em", "100em", 100),
16         ("junk", "#!?", None),
17         ("trailingjunk", "100#!?", 100),
18     ]
19     def gen(name, string, exp, code):
20         testing = ["size.nonnegativeinteger"]
21         if exp is None:
22             testing.append("size.error")
23             code += "@assert canvas.width === 300;\n@assert canvas.height === 150;\n"
24             expected = "size 300 150"
25         else:
26             code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp)
27             expected = "size %s %s" % (exp, exp)
29             # With "100%", Opera gets canvas.width = 100 but renders at 100% of the frame width,
30             # so check the CSS display width
31             code += '@assert window.getComputedStyle(canvas, null).getPropertyValue("width") === "%spx";\n' % (exp, )
33         code += "@assert canvas.getAttribute('width') === %r;\n" % string
34         code += "@assert canvas.getAttribute('height') === %r;\n" % string
36         if exp == 0:
37             expected = None # can't generate zero-sized PNGs for the expected image
39         return code, testing, expected
41     for name, string, exp in cases:
42         code = ""
43         code, testing, expected = gen(name, string, exp, code)
44         # We need to replace \r with 
 because \r\n gets converted to \n in the HTML parser.
45         htmlString = string.replace('\r', '
')
46         tests.append( {
47             "name": "size.attributes.parse.%s" % name,
48             "desc": "Parsing of non-negative integers",
49             "testing": testing,
50             "canvas": 'width="%s" height="%s"' % (htmlString, htmlString),
51             "code": code,
52             "expected": expected
53         } )
55     for name, string, exp in cases:
56         code = "canvas.setAttribute('width', %r);\ncanvas.setAttribute('height', %r);\n" % (string, string)
57         code, testing, expected = gen(name, string, exp, code)
58         tests.append( {
59             "name": "size.attributes.setAttribute.%s" % name,
60             "desc": "Parsing of non-negative integers in setAttribute",
61             "testing": testing,
62             "canvas": 'width="50" height="50"',
63             "code": code,
64             "expected": expected
65         } )
67 - meta: |
68     state = [ # some non-default values to test with
69         ('strokeStyle', '"#ff0000"'),
70         ('fillStyle', '"#ff0000"'),
71         ('globalAlpha', 0.5),
72         ('lineWidth', 0.5),
73         ('lineCap', '"round"'),
74         ('lineJoin', '"round"'),
75         ('miterLimit', 0.5),
76         ('shadowOffsetX', 5),
77         ('shadowOffsetY', 5),
78         ('shadowBlur', 5),
79         ('shadowColor', '"#ff0000"'),
80         ('globalCompositeOperation', '"copy"'),
81         ('font', '"25px serif"'),
82         ('textAlign', '"center"'),
83         ('textBaseline', '"bottom"'),
84     ]
85     for key,value in state:
86         tests.append( {
87             'name': '2d.state.saverestore.%s' % key,
88             'desc': 'save()/restore() works for %s' % key,
89             'testing': [ '2d.state.%s' % key ],
90             'code':
91     """// Test that restore() undoes any modifications
92     var old = ctx.%(key)s;
93     ctx.save();
94     ctx.%(key)s = %(value)s;
95     ctx.restore();
96     @assert ctx.%(key)s === old;
98     // Also test that save() doesn't modify the values
99     ctx.%(key)s = %(value)s;
100     old = ctx.%(key)s;
101         // we're not interested in failures caused by get(set(x)) != x (e.g.
102         // from rounding), so compare against 'old' instead of against %(value)s
103     ctx.save();
104     @assert ctx.%(key)s === old;
105     ctx.restore();
106     """ % { 'key':key, 'value':value }
107         } )
109     tests.append( {
110         'name': 'initial.reset.2dstate',
111         'desc': 'Resetting the canvas state resets 2D state variables',
112         'testing': [ 'initial.reset' ],
113         'code':
114     """canvas.width = 100;
115     var default_val;
116     """ + "".join(
117     """
118     default_val = ctx.%(key)s;
119     ctx.%(key)s = %(value)s;
120     canvas.width = 100;
121     @assert ctx.%(key)s === default_val;
122     """ % { 'key':key, 'value':value }
123         for key,value in state),
124     } )
126 - meta: |
127     # Composite operation tests
128     # <http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2007-March/010608.html>
129     ops = [
130         # name               FA      FB
131         ('source-over',      '1',    '1-aA'),
132         ('destination-over', '1-aB', '1'),
133         ('source-in',        'aB',   '0'),
134         ('destination-in',   '0',    'aA'),
135         ('source-out',       '1-aB', '0'),
136         ('destination-out',  '0',    '1-aA'),
137         ('source-atop',      'aB',   '1-aA'),
138         ('destination-atop', '1-aB', 'aA'),
139         ('xor',              '1-aB', '1-aA'),
140         ('copy',             '1',    '0'),
141         ('lighter',          '1',    '1'),
142     ]
144     # The ones that change the output when src = (0,0,0,0):
145     ops_trans = [ 'source-in', 'destination-in', 'source-out', 'destination-atop', 'copy' ];
147     def calc_output(A, B, FA_code, FB_code):
148         (RA, GA, BA, aA) = A
149         (RB, GB, BB, aB) = B
150         rA, gA, bA = RA*aA, GA*aA, BA*aA
151         rB, gB, bB = RB*aB, GB*aB, BB*aB
153         FA = eval(FA_code)
154         FB = eval(FB_code)
156         rO = rA*FA + rB*FB
157         gO = gA*FA + gB*FB
158         bO = bA*FA + bB*FB
159         aO = aA*FA + aB*FB
161         rO = min(255, rO)
162         gO = min(255, gO)
163         bO = min(255, bO)
164         aO = min(1, aO)
166         if aO:
167             RO = rO / aO
168             GO = gO / aO
169             BO = bO / aO
170         else: RO = GO = BO = 0
172         return (RO, GO, BO, aO)
174     def to_test(color):
175         r, g, b, a = color
176         return '%d,%d,%d,%d' % (round(r), round(g), round(b), round(a*255))
177     def to_cairo(color):
178         r, g, b, a = color
179         return '%f,%f,%f,%f' % (r/255., g/255., b/255., a)
181     for (name, src, dest) in [
182         ('solid', (255, 255, 0, 1.0), (0, 255, 255, 1.0)),
183         ('transparent', (0, 0, 255, 0.75), (0, 255, 0, 0.5)),
184             # catches the atop, xor and lighter bugs in Opera 9.10
185     ]:
186         for op, FA_code, FB_code in ops:
187             expected = calc_output(src, dest, FA_code, FB_code)
188             tests.append( {
189                 'name': '2d.composite.%s.%s' % (name, op),
190                 'testing': [ '2d.composite.%s' % op ],
191                 'code': """
192     ctx.fillStyle = 'rgba%s';
193     ctx.fillRect(0, 0, 100, 50);
194     ctx.globalCompositeOperation = '%s';
195     ctx.fillStyle = 'rgba%s';
196     ctx.fillRect(0, 0, 100, 50);
197     @assert pixel 50,25 ==~ %s +/- 5;
198     """ % (dest, op, src, to_test(expected)),
199                 'expected': """size 100 50
200     cr.set_source_rgba(%s)
201     cr.rectangle(0, 0, 100, 50)
202     cr.fill()
203     """ % to_cairo(expected),
204             } )
206     for (name, src, dest) in [ ('image', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
207         for op, FA_code, FB_code in ops:
208             expected = calc_output(src, dest, FA_code, FB_code)
209             tests.append( {
210                 'name': '2d.composite.%s.%s' % (name, op),
211                 'testing': [ '2d.composite.%s' % op ],
212                 'images': [ 'yellow75.png' ],
213                 'code': """
214     ctx.fillStyle = 'rgba%s';
215     ctx.fillRect(0, 0, 100, 50);
216     ctx.globalCompositeOperation = '%s';
217     ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
218     @assert pixel 50,25 ==~ %s +/- 5;
219     """ % (dest, op, to_test(expected)),
220                 'expected': """size 100 50
221     cr.set_source_rgba(%s)
222     cr.rectangle(0, 0, 100, 50)
223     cr.fill()
224     """ % to_cairo(expected),
225             } )
227     for (name, src, dest) in [ ('canvas', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
228         for op, FA_code, FB_code in ops:
229             expected = calc_output(src, dest, FA_code, FB_code)
230             tests.append( {
231                 'name': '2d.composite.%s.%s' % (name, op),
232                 'testing': [ '2d.composite.%s' % op ],
233                 'images': [ 'yellow75.png' ],
234                 'code': """
235     var canvas2 = document.createElement('canvas');
236     canvas2.width = canvas.width;
237     canvas2.height = canvas.height;
238     var ctx2 = canvas2.getContext('2d');
239     ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
240     ctx.fillStyle = 'rgba%s';
241     ctx.fillRect(0, 0, 100, 50);
242     ctx.globalCompositeOperation = '%s';
243     ctx.drawImage(canvas2, 0, 0);
244     @assert pixel 50,25 ==~ %s +/- 5;
245     """ % (dest, op, to_test(expected)),
246                 'expected': """size 100 50
247     cr.set_source_rgba(%s)
248     cr.rectangle(0, 0, 100, 50)
249     cr.fill()
250     """ % to_cairo(expected),
251             } )
254     for (name, src, dest) in [ ('uncovered.fill', (0, 0, 255, 0.75), (0, 255, 0, 0.5)) ]:
255         for op, FA_code, FB_code in ops:
256             if op not in ops_trans: continue
257             expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
258             tests.append( {
259                 'name': '2d.composite.%s.%s' % (name, op),
260                 'desc': 'fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
261                 'testing': [ '2d.composite.%s' % op ],
262                 'code': """
263     ctx.fillStyle = 'rgba%s';
264     ctx.fillRect(0, 0, 100, 50);
265     ctx.globalCompositeOperation = '%s';
266     ctx.fillStyle = 'rgba%s';
267     ctx.translate(0, 25);
268     ctx.fillRect(0, 50, 100, 50);
269     @assert pixel 50,25 ==~ %s +/- 5;
270     """ % (dest, op, src, to_test(expected0)),
271                 'expected': """size 100 50
272     cr.set_source_rgba(%s)
273     cr.rectangle(0, 0, 100, 50)
274     cr.fill()
275     """ % (to_cairo(expected0)),
276             } )
278     for (name, src, dest) in [ ('uncovered.image', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
279         for op, FA_code, FB_code in ops:
280             if op not in ops_trans: continue
281             expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
282             tests.append( {
283                 'name': '2d.composite.%s.%s' % (name, op),
284                 'desc': 'drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
285                 'testing': [ '2d.composite.%s' % op ],
286                 'images': [ 'yellow.png' ],
287                 'code': """
288     ctx.fillStyle = 'rgba%s';
289     ctx.fillRect(0, 0, 100, 50);
290     ctx.globalCompositeOperation = '%s';
291     ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
292     @assert pixel 15,15 ==~ %s +/- 5;
293     @assert pixel 50,25 ==~ %s +/- 5;
294     """ % (dest, op, to_test(expected0), to_test(expected0)),
295                 'expected': """size 100 50
296     cr.set_source_rgba(%s)
297     cr.rectangle(0, 0, 100, 50)
298     cr.fill()
299     """ % (to_cairo(expected0)),
300             } )
302     for (name, src, dest) in [ ('uncovered.nocontext', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
303         for op, FA_code, FB_code in ops:
304             if op not in ops_trans: continue
305             expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
306             tests.append( {
307                 'name': '2d.composite.%s.%s' % (name, op),
308                 'desc': 'drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.',
309                 'testing': [ '2d.composite.%s' % op ],
310                 'code': """
311     ctx.fillStyle = 'rgba%s';
312     ctx.fillRect(0, 0, 100, 50);
313     ctx.globalCompositeOperation = '%s';
314     var canvas2 = document.createElement('canvas');
315     ctx.drawImage(canvas2, 0, 0);
316     @assert pixel 50,25 ==~ %s +/- 5;
317     """ % (dest, op, to_test(expected0)),
318                 'expected': """size 100 50
319     cr.set_source_rgba(%s)
320     cr.rectangle(0, 0, 100, 50)
321     cr.fill()
322     """ % (to_cairo(expected0)),
323             } )
325     for (name, src, dest) in [ ('uncovered.pattern', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
326         for op, FA_code, FB_code in ops:
327             if op not in ops_trans: continue
328             expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
329             tests.append( {
330                 'name': '2d.composite.%s.%s' % (name, op),
331                 'desc': 'Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
332                 'testing': [ '2d.composite.%s' % op ],
333                 'images': [ 'yellow.png' ],
334                 'code': """
335     ctx.fillStyle = 'rgba%s';
336     ctx.fillRect(0, 0, 100, 50);
337     ctx.globalCompositeOperation = '%s';
338     ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
339     ctx.fillRect(0, 50, 100, 50);
340     @assert pixel 50,25 ==~ %s +/- 5;
341     """ % (dest, op, to_test(expected0)),
342                 'expected': """size 100 50
343     cr.set_source_rgba(%s)
344     cr.rectangle(0, 0, 100, 50)
345     cr.fill()
346     """ % (to_cairo(expected0)),
347             } )
349     for op, FA_code, FB_code in ops:
350         tests.append( {
351             'name': '2d.composite.clip.%s' % (op),
352             'desc': 'fill() does not affect pixels outside the clip region.',
353             'testing': [ '2d.composite.%s' % op ],
354             'code': """
355     ctx.fillStyle = '#0f0';
356     ctx.fillRect(0, 0, 100, 50);
357     ctx.globalCompositeOperation = '%s';
358     ctx.rect(-20, -20, 10, 10);
359     ctx.clip();
360     ctx.fillStyle = '#f00';
361     ctx.fillRect(0, 0, 50, 50);
362     @assert pixel 25,25 == 0,255,0,255;
363     @assert pixel 75,25 == 0,255,0,255;
364     """ % (op),
365                 'expected': 'green'
366         } )
368 - meta: |
369     # Color parsing tests
371     # Try most of the CSS3 Color <color> values - http://www.w3.org/TR/css3-color/#colorunits
372     big_float = '1' + ('0' * 39)
373     big_double = '1' + ('0' * 310)
374     for name, string, r,g,b,a, notes in [
375         ('html4', 'limE', 0,255,0,255, ""),
376         ('hex3', '#0f0', 0,255,0,255, ""),
377         ('hex4', '#0f0f', 0,255,0,255, ""),
378         ('hex6', '#00fF00', 0,255,0,255, ""),
379         ('hex8', '#00ff00ff', 0,255,0,255, ""),
380         ('rgb-num', 'rgb(0,255,0)', 0,255,0,255, ""),
381         ('rgb-clamp-1', 'rgb(-1000, 1000, -1000)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
382         ('rgb-clamp-2', 'rgb(-200%, 200%, -200%)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
383         ('rgb-clamp-3', 'rgb(-2147483649, 4294967298, -18446744073709551619)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
384         ('rgb-clamp-4', 'rgb(-'+big_float+', '+big_float+', -'+big_float+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
385         ('rgb-clamp-5', 'rgb(-'+big_double+', '+big_double+', -'+big_double+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
386         ('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...)'),
387         ('rgb-eof', 'rgb(0, 255, 0', 0,255,0,255, ""), # see CSS2.1 4.2 "Unexpected end of style sheet"
388         ('rgba-solid-1', 'rgba(  0  ,  255  ,  0  ,  1  )', 0,255,0,255, ""),
389         ('rgba-solid-2', 'rgba(  0  ,  255  ,  0  ,  1.0  )', 0,255,0,255, ""),
390         ('rgba-solid-3', 'rgba(  0  ,  255  ,  0  , +1  )', 0,255,0,255, ""),
391         ('rgba-solid-4', 'rgba( -0  ,  255  , +0  ,  1  )', 0,255,0,255, ""),
392         ('rgba-num-1', 'rgba(  0  ,  255  ,  0  ,  .499  )', 0,255,0,127, ""),
393         ('rgba-num-2', 'rgba(  0  ,  255  ,  0  ,  0.499  )', 0,255,0,127, ""),
394         ('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
395         ('rgba-clamp-1', 'rgba(0, 255, 0, -2)', 0,0,0,0, ""),
396         ('rgba-clamp-2', 'rgba(0, 255, 0, 2)', 0,255,0,255, ""),
397         ('rgba-eof', 'rgba(0, 255, 0, 1', 0,255,0,255, ""),
398         ('transparent-1', 'transparent', 0,0,0,0, ""),
399         ('transparent-2', 'TrAnSpArEnT', 0,0,0,0, ""),
400         ('hsl-1', 'hsl(120, 100%, 50%)', 0,255,0,255, ""),
401         ('hsl-2', 'hsl( -240 , 100% , 50% )', 0,255,0,255, ""),
402         ('hsl-3', 'hsl(360120, 100%, 50%)', 0,255,0,255, ""),
403         ('hsl-4', 'hsl(-360240, 100%, 50%)', 0,255,0,255, ""),
404         ('hsl-5', 'hsl(120.0, 100.0%, 50.0%)', 0,255,0,255, ""),
405         ('hsl-6', 'hsl(+120, +100%, +50%)', 0,255,0,255, ""),
406         ('hsl-clamp-1', 'hsl(120, 200%, 50%)', 0,255,0,255, ""),
407         ('hsl-clamp-2', 'hsl(120, -200%, 49.9%)', 127,127,127,255, ""),
408         ('hsl-clamp-3', 'hsl(120, 100%, 200%)', 255,255,255,255, ""),
409         ('hsl-clamp-4', 'hsl(120, 100%, -200%)', 0,0,0,255, ""),
410         ('hsla-1', 'hsla(120, 100%, 50%, 0.499)', 0,255,0,127, ""),
411         ('hsla-2', 'hsla( 120.0 , 100.0% , 50.0% , 1 )', 0,255,0,255, ""),
412         ('hsla-clamp-1', 'hsla(120, 200%, 50%, 1)', 0,255,0,255, ""),
413         ('hsla-clamp-2', 'hsla(120, -200%, 49.9%, 1)', 127,127,127,255, ""),
414         ('hsla-clamp-3', 'hsla(120, 100%, 200%, 1)', 255,255,255,255, ""),
415         ('hsla-clamp-4', 'hsla(120, 100%, -200%, 1)', 0,0,0,255, ""),
416         ('hsla-clamp-5', 'hsla(120, 100%, 50%, 2)', 0,255,0,255, ""),
417         ('hsla-clamp-6', 'hsla(120, 100%, 0%, -2)', 0,0,0,0, ""),
418         ('svg-1', 'gray', 128,128,128,255, ""),
419         ('svg-2', 'grey', 128,128,128,255, ""),
420         # css-color-4 rgb() color function
421         #   https://drafts.csswg.org/css-color/#numeric-rgb
422         ('css-color-4-rgb-1', 'rgb(0, 255.0, 0)', 0,255,0,255, ""),
423         ('css-color-4-rgb-2', 'rgb(0, 255, 0, 0.2)', 0,255,0,51, ""),
424         ('css-color-4-rgb-3', 'rgb(0, 255, 0, 20%)', 0,255,0,51, ""),
425         ('css-color-4-rgb-4', 'rgb(0 255 0)', 0,255,0,255, ""),
426         ('css-color-4-rgb-5', 'rgb(0 255 0 / 0.2)', 0,255,0,51, ""),
427         ('css-color-4-rgb-6', 'rgb(0 255 0 / 20%)', 0,255,0,51, ""),
428         ('css-color-4-rgba-1', 'rgba(0, 255.0, 0)', 0,255,0,255, ""),
429         ('css-color-4-rgba-2', 'rgba(0, 255, 0, 0.2)', 0,255,0,51, ""),
430         ('css-color-4-rgba-3', 'rgba(0, 255, 0, 20%)', 0,255,0,51, ""),
431         ('css-color-4-rgba-4', 'rgba(0 255 0)', 0,255,0,255, ""),
432         ('css-color-4-rgba-5', 'rgba(0 255 0 / 0.2)', 0,255,0,51, ""),
433         ('css-color-4-rgba-6', 'rgba(0 255 0 / 20%)', 0,255,0,51, ""),
434         # css-color-4 hsl() color function
435         #   https://drafts.csswg.org/css-color/#the-hsl-notation
436         ('css-color-4-hsl-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
437         ('css-color-4-hsl-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
438         ('css-color-4-hsl-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
439         ('css-color-4-hsl-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
440         ('css-color-4-hsl-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
441         ('css-color-4-hsl-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
442         ('css-color-4-hsl-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
443         ('css-color-4-hsl-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
444         ('css-color-4-hsl-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
445         ('css-color-4-hsla-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
446         ('css-color-4-hsla-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
447         ('css-color-4-hsla-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
448         ('css-color-4-hsla-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
449         ('css-color-4-hsla-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
450         ('css-color-4-hsla-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
451         ('css-color-4-hsla-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
452         ('css-color-4-hsla-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
453         ('css-color-4-hsla-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
454         # currentColor is handled later
455     ]:
456         # TODO: test by retrieving fillStyle, instead of actually drawing?
457         # TODO: test strokeStyle, shadowColor in the same way
458         test = {
459             'name': '2d.fillStyle.parse.%s' % name,
460             'testing': [ '2d.colors.parse' ],
461             'notes': notes,
462             'code': """
463     ctx.fillStyle = '#f00';
464     ctx.fillStyle = '%s';
465     ctx.fillRect(0, 0, 100, 50);
466     @assert pixel 50,25 == %d,%d,%d,%d;
467     """ % (string, r,g,b,a),
468             'expected': """size 100 50
469     cr.set_source_rgba(%f, %f, %f, %f)
470     cr.rectangle(0, 0, 100, 50)
471     cr.fill()
472     """ % (r/255., g/255., b/255., a/255.),
473         }
474         tests.append(test)
476     # Also test that invalid colors are ignored
477     for name, string in [
478         ('hex1', '#f'),
479         ('hex2', '#f0'),
480         ('hex3', '#g00'),
481         ('hex4', '#fg00'),
482         ('hex5', '#ff000'),
483         ('hex6', '#fg0000'),
484         ('hex7', '#ff0000f'),
485         ('hex8', '#fg0000ff'),
486         ('rgb-1', 'rgb(255.0, 0, 0,)'),
487         ('rgb-2', 'rgb(100%, 0, 0)'),
488         ('rgb-3', 'rgb(255, - 1, 0)'),
489         ('rgba-1', 'rgba(100%, 0, 0, 1)'),
490         ('rgba-2', 'rgba(255, 0, 0, 1. 0)'),
491         ('rgba-3', 'rgba(255, 0, 0, 1.)'),
492         ('rgba-4', 'rgba(255, 0, 0, '),
493         ('rgba-5', 'rgba(255, 0, 0, 1,)'),
494         ('hsl-1', 'hsl(0%, 100%, 50%)'),
495         ('hsl-2', 'hsl(z, 100%, 50%)'),
496         ('hsl-3', 'hsl(0, 0, 50%)'),
497         ('hsl-4', 'hsl(0, 100%, 0)'),
498         ('hsl-5', 'hsl(0, 100.%, 50%)'),
499         ('hsl-6', 'hsl(0, 100%, 50%,)'),
500         ('hsla-1', 'hsla(0%, 100%, 50%, 1)'),
501         ('hsla-2', 'hsla(0, 0, 50%, 1)'),
502         ('hsla-3', 'hsla(0, 0, 50%, 1,)'),
503         ('name-1', 'darkbrown'),
504         ('name-2', 'firebrick1'),
505         ('name-3', 'red blue'),
506         ('name-4', '"red"'),
507         ('name-5', '"red'),
508         # css-color-4 color function
509         #   comma and comma-less expressions should not mix together.
510         ('css-color-4-rgb-1', 'rgb(255, 0, 0 / 1)'),
511         ('css-color-4-rgb-2', 'rgb(255 0 0, 1)'),
512         ('css-color-4-rgb-3', 'rgb(255, 0 0)'),
513         ('css-color-4-rgba-1', 'rgba(255, 0, 0 / 1)'),
514         ('css-color-4-rgba-2', 'rgba(255 0 0, 1)'),
515         ('css-color-4-rgba-3', 'rgba(255, 0 0)'),
516         ('css-color-4-hsl-1', 'hsl(0, 100%, 50% / 1)'),
517         ('css-color-4-hsl-2', 'hsl(0 100% 50%, 1)'),
518         ('css-color-4-hsl-3', 'hsl(0, 100% 50%)'),
519         ('css-color-4-hsla-1', 'hsla(0, 100%, 50% / 1)'),
520         ('css-color-4-hsla-2', 'hsla(0 100% 50%, 1)'),
521         ('css-color-4-hsla-3', 'hsla(0, 100% 50%)'),
522         #  trailing slash
523         ('css-color-4-rgb-4', 'rgb(0 0 0 /)'),
524         ('css-color-4-rgb-5', 'rgb(0, 0, 0 /)'),
525         ('css-color-4-hsl-4', 'hsl(0 100% 50% /)'),
526         ('css-color-4-hsl-5', 'hsl(0, 100%, 50% /)'),
527     ]:
528         test = {
529             'name': '2d.fillStyle.parse.invalid.%s' % name,
530             'testing': [ '2d.colors.parse' ],
531             'code': """
532     ctx.fillStyle = '#0f0';
533     try { ctx.fillStyle = '%s'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
534     ctx.fillRect(0, 0, 100, 50);
535     @assert pixel 50,25 == 0,255,0,255;
536     """ % string,
537             'expected': 'green'
538         }
539         tests.append(test)
541     # Some can't have positive tests, only negative tests, because we don't know what color they're meant to be
542     for name, string in [
543         ('system', 'ThreeDDarkShadow'),
544         #('flavor', 'flavor'), # removed from latest CSS3 Color drafts
545     ]:
546         test = {
547             'name': '2d.fillStyle.parse.%s' % name,
548             'testing': [ '2d.colors.parse' ],
549             'code': """
550     ctx.fillStyle = '#f00';
551     ctx.fillStyle = '%s';
552     @assert ctx.fillStyle =~ /^#(?!(FF0000|ff0000|f00)$)/; // test that it's not red
553     """ % (string,),
554         }
555         tests.append(test)