summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/2dcontext/tools/gentest.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/2dcontext/tools/gentest.py')
-rw-r--r--testing/web-platform/tests/2dcontext/tools/gentest.py787
1 files changed, 0 insertions, 787 deletions
diff --git a/testing/web-platform/tests/2dcontext/tools/gentest.py b/testing/web-platform/tests/2dcontext/tools/gentest.py
deleted file mode 100644
index 079f476d2..000000000
--- a/testing/web-platform/tests/2dcontext/tools/gentest.py
+++ /dev/null
@@ -1,787 +0,0 @@
-# Copyright (c) 2010 Philip Taylor
-# Released under the BSD license and W3C Test Suite License: see LICENSE.txt
-
-# Current code status:
-#
-# This was originally written for use at
-# http://philip.html5.org/tests/canvas/suite/tests/
-#
-# It has been adapted for use with the Web Platform Test Suite suite at
-# https://github.com/w3c/web-platform-tests/
-#
-# The W3C version excludes a number of features (multiple versions of each test
-# case of varying verbosity, Mozilla mochitests, semi-automated test harness)
-# to focus on simply providing reviewable test cases. It also expects a different
-# directory structure.
-# This code attempts to support both versions, but the non-W3C version hasn't
-# been tested recently and is probably broken.
-
-# To update or add test cases:
-#
-# * Modify the tests*.yaml files.
-# 'name' is an arbitrary hierarchical name to help categorise tests.
-# 'desc' is a rough description of what behaviour the test aims to test.
-# 'testing' is a list of references to spec.yaml, to show which spec sentences
-# this test case is primarily testing.
-# 'code' is JavaScript code to execute, with some special commands starting with '@'
-# 'expected' is what the final canvas output should be: a string 'green' or 'clear'
-# (100x50 images in both cases), or a string 'size 100 50' (or any other size)
-# followed by Python code using Pycairo to generate the image.
-#
-# * Run "python gentest.py".
-# This requires a few Python modules which might not be ubiquitous.
-# It has only been tested on Linux.
-# It will usually emit some warnings, which ideally should be fixed but can
-# generally be safely ignored.
-#
-# * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
-
-import re
-import codecs
-import time
-import os
-import shutil
-import sys
-import xml.dom.minidom
-from xml.dom.minidom import Node
-
-import cairo
-
-try:
- import syck as yaml # compatible and lots faster
-except ImportError:
- import yaml
-
-# Default mode is for the W3C test suite; the --standalone option
-# generates various extra files that aren't needed there
-W3CMODE = True
-if '--standalone' in sys.argv:
- W3CMODE = False
-
-TESTOUTPUTDIR = '../../2dcontext'
-IMAGEOUTPUTDIR = '../../2dcontext'
-MISCOUTPUTDIR = './output'
-SPECOUTPUTDIR = '../../annotated-spec'
-
-SPECOUTPUTPATH = '../annotated-spec' # relative to TESTOUTPUTDIR
-
-def simpleEscapeJS(str):
- return str.replace('\\', '\\\\').replace('"', '\\"')
-
-def escapeJS(str):
- str = simpleEscapeJS(str)
- str = re.sub(r'\[(\w+)\]', r'[\\""+(\1)+"\\"]', str) # kind of an ugly hack, for nicer failure-message output
- return str
-
-def escapeHTML(str):
- return str.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
-
-def expand_nonfinite(method, argstr, tail):
- """
- >>> print expand_nonfinite('f', '<0 a>, <0 b>', ';')
- f(a, 0);
- f(0, b);
- f(a, b);
- >>> print expand_nonfinite('f', '<0 a>, <0 b c>, <0 d>', ';')
- f(a, 0, 0);
- f(0, b, 0);
- f(0, c, 0);
- f(0, 0, d);
- f(a, b, 0);
- f(a, b, d);
- f(a, 0, d);
- f(0, b, d);
- """
- # argstr is "<valid-1 invalid1-1 invalid2-1 ...>, ..." (where usually
- # 'invalid' is Infinity/-Infinity/NaN)
- args = []
- for arg in argstr.split(', '):
- a = re.match('<(.*)>', arg).group(1)
- args.append(a.split(' '))
- calls = []
- # Start with the valid argument list
- call = [ args[j][0] for j in range(len(args)) ]
- # For each argument alone, try setting it to all its invalid values:
- for i in range(len(args)):
- for a in args[i][1:]:
- c2 = call[:]
- c2[i] = a
- calls.append(c2)
- # For all combinations of >= 2 arguments, try setting them to their
- # first invalid values. (Don't do all invalid values, because the
- # number of combinations explodes.)
- def f(c, start, depth):
- for i in range(start, len(args)):
- if len(args[i]) > 1:
- a = args[i][1]
- c2 = c[:]
- c2[i] = a
- if depth > 0: calls.append(c2)
- f(c2, i+1, depth+1)
- f(call, 0, 0)
-
- return '\n'.join('%s(%s)%s' % (method, ', '.join(c), tail) for c in calls)
-
-# Run with --test argument to run unit tests
-if len(sys.argv) > 1 and sys.argv[1] == '--test':
- import doctest
- doctest.testmod()
- sys.exit()
-
-templates = yaml.load(open('templates.yaml', "r").read())
-name_mapping = yaml.load(open('name2dir.yaml', "r").read())
-
-spec_assertions = []
-for s in yaml.load(open('spec.yaml', "r").read())['assertions']:
- if 'meta' in s:
- eval(compile(s['meta'], '<meta spec assertion>', 'exec'), {}, {'assertions':spec_assertions})
- else:
- spec_assertions.append(s)
-
-tests = []
-for t in sum([ yaml.load(open(f, "r").read()) for f in ['tests.yaml', 'tests2d.yaml', 'tests2dtext.yaml']], []):
- if 'DISABLED' in t:
- continue
- if 'meta' in t:
- eval(compile(t['meta'], '<meta test>', 'exec'), {}, {'tests':tests})
- else:
- tests.append(t)
-
-category_names = []
-category_contents_direct = {}
-category_contents_all = {}
-
-spec_ids = {}
-for t in spec_assertions: spec_ids[t['id']] = True
-spec_refs = {}
-
-def backref_html(name):
- backrefs = []
- c = ''
- for p in name.split('.')[:-1]:
- c += '.'+p
- backrefs.append('<a href="index%s.html">%s</a>.' % (c, p))
- backrefs.append(name.split('.')[-1])
- return ''.join(backrefs)
-
-def make_flat_image(filename, w, h, r,g,b,a):
- if os.path.exists('%s/%s' % (IMAGEOUTPUTDIR, filename)):
- return filename
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
- cr = cairo.Context(surface)
- cr.set_source_rgba(r, g, b, a)
- cr.rectangle(0, 0, w, h)
- cr.fill()
- surface.write_to_png('%s/%s' % (IMAGEOUTPUTDIR, filename))
- return filename
-
-# Ensure the test output directories exist
-testdirs = [TESTOUTPUTDIR, IMAGEOUTPUTDIR, MISCOUTPUTDIR]
-if not W3CMODE: testdirs.append('%s/mochitests' % MISCOUTPUTDIR)
-else:
- for map_dir in set(name_mapping.values()):
- testdirs.append("%s/%s" % (TESTOUTPUTDIR, map_dir))
-for d in testdirs:
- try: os.mkdir(d)
- except: pass # ignore if it already exists
-
-mochitests = []
-used_images = {}
-
-def expand_test_code(code):
- code = re.sub(r'@nonfinite ([^(]+)\(([^)]+)\)(.*)', lambda m: expand_nonfinite(m.group(1), m.group(2), m.group(3)), code) # must come before '@assert throws'
-
- code = re.sub(r'@assert pixel (\d+,\d+) == (\d+,\d+,\d+,\d+);',
- r'_assertPixel(canvas, \1, \2, "\1", "\2");',
- code)
-
- code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+);',
- r'_assertPixelApprox(canvas, \1, \2, "\1", "\2", 2);',
- code)
-
- code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+) \+/- (\d+);',
- r'_assertPixelApprox(canvas, \1, \2, "\1", "\2", \3);',
- code)
-
- code = re.sub(r'@assert throws (\S+_ERR) (.*);',
- r'assert_throws("\1", function() { \2; });',
- code)
-
- code = re.sub(r'@assert throws (\S+Error) (.*);',
- r'assert_throws(new \1(), function() { \2; });',
- code)
-
- code = re.sub(r'@assert throws (.*);',
- r'assert_throws(null, function() { \1; });',
- code)
-
- code = re.sub(r'@assert (.*) === (.*);',
- lambda m: '_assertSame(%s, %s, "%s", "%s");'
- % (m.group(1), m.group(2), escapeJS(m.group(1)), escapeJS(m.group(2)))
- , code)
-
- code = re.sub(r'@assert (.*) !== (.*);',
- lambda m: '_assertDifferent(%s, %s, "%s", "%s");'
- % (m.group(1), m.group(2), escapeJS(m.group(1)), escapeJS(m.group(2)))
- , code)
-
- code = re.sub(r'@assert (.*) =~ (.*);',
- lambda m: 'assert_regexp_match(%s, %s);'
- % (m.group(1), m.group(2))
- , code)
-
- code = re.sub(r'@assert (.*);',
- lambda m: '_assert(%s, "%s");'
- % (m.group(1), escapeJS(m.group(1)))
- , code)
-
- code = re.sub(r' @moz-todo', '', code)
-
- code = re.sub(r'@moz-UniversalBrowserRead;',
- ""
- , code)
-
- assert('@' not in code)
-
- return code
-
-def expand_mochitest_code(code):
- code = re.sub(r'@nonfinite ([^(]+)\(([^)]+)\)(.*)', lambda m: expand_nonfinite(m.group(1), m.group(2), m.group(3)), code)
-
- code = re.sub(r'@assert pixel (\d+,\d+) == (\d+,\d+,\d+,\d+);',
- r'isPixel(ctx, \1, \2, "\1", "\2", 0);',
- code)
-
- code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+);',
- r'isPixel(ctx, \1, \2, "\1", "\2", 2);',
- code)
-
- code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+) \+/- (\d+);',
- r'isPixel(ctx, \1, \2, "\1", "\2", \3);',
- code)
-
- code = re.sub(r'@assert throws (\S+_ERR) (.*);',
- lambda m: 'var _thrown = undefined; try {\n %s;\n} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.%s, "should throw %s");'
- % (m.group(2), m.group(1), m.group(1))
- , code)
-
- code = re.sub(r'@assert throws (\S+Error) (.*);',
- lambda m: 'var _thrown = undefined; try {\n %s;\n} catch (e) { _thrown = e }; ok(_thrown && (_thrown instanceof %s), "should throw %s");'
- % (m.group(2), m.group(1), m.group(1))
- , code)
-
- code = re.sub(r'@assert throws (.*);',
- lambda m: 'try { var _thrown = false;\n %s;\n} catch (e) { _thrown = true; } finally { ok(_thrown, "should throw exception"); }'
- % (m.group(1))
- , code)
-
- code = re.sub(r'@assert (.*) =~ (.*);',
- lambda m: 'ok(%s.match(%s), "%s.match(%s)");'
- % (m.group(1), m.group(2), escapeJS(m.group(1)), escapeJS(m.group(2)))
- , code)
-
- code = re.sub(r'@assert (.*);',
- lambda m: 'ok(%s, "%s");'
- % (m.group(1), escapeJS(m.group(1)))
- , code)
-
- code = re.sub(r'((?:^|\n|;)\s*)ok(.*;) @moz-todo',
- lambda m: '%stodo%s'
- % (m.group(1), m.group(2))
- , code)
-
- code = re.sub(r'((?:^|\n|;)\s*)(is.*;) @moz-todo',
- lambda m: '%stodo_%s'
- % (m.group(1), m.group(2))
- , code)
-
- code = re.sub(r'@moz-UniversalBrowserRead;',
- "netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');"
- , code)
-
- code = code.replace('../images/', 'image_')
-
- assert '@' not in code, '@ not in code:\n%s' % code
-
- return code
-
-used_tests = {}
-for i in range(len(tests)):
- test = tests[i]
-
- name = test['name']
- print "\r(%s)" % name, " "*32, "\t",
-
- if name in used_tests:
- print "Test %s is defined twice" % name
- used_tests[name] = 1
-
- mapped_name = None
- for mn in sorted(name_mapping.keys(), key=len, reverse=True):
- if name.startswith(mn):
- mapped_name = "%s/%s" % (name_mapping[mn], name)
- break
- if not mapped_name:
- print "LIKELY ERROR: %s has no defined target directory mapping" % name
- mapped_name = name
- if 'manual' in test:
- mapped_name += "-manual"
-
- cat_total = ''
- for cat_part in [''] + name.split('.')[:-1]:
- cat_total += cat_part+'.'
- if not cat_total in category_names: category_names.append(cat_total)
- category_contents_all.setdefault(cat_total, []).append(name)
- category_contents_direct.setdefault(cat_total, []).append(name)
-
- for ref in test.get('testing', []):
- if ref not in spec_ids:
- print "Test %s uses nonexistent spec point %s" % (name, ref)
- spec_refs.setdefault(ref, []).append(name)
- #if not (len(test.get('testing', [])) or 'mozilla' in test):
- if not test.get('testing', []):
- print "Test %s doesn't refer to any spec points" % name
-
- if test.get('expected', '') == 'green' and re.search(r'@assert pixel .* 0,0,0,0;', test['code']):
- print "Probable incorrect pixel test in %s" % name
-
- code = expand_test_code(test['code'])
-
- mochitest = not (W3CMODE or 'manual' in test or 'disabled' in test.get('mozilla', {}))
- if mochitest:
- mochi_code = expand_mochitest_code(test['code'])
-
- mochi_name = name
- if 'mozilla' in test:
- if 'throws' in test['mozilla']:
- mochi_code = templates['mochitest.exception'] % mochi_code
- if 'bug' in test['mozilla']:
- mochi_name = "%s - bug %s" % (name, test['mozilla']['bug'])
-
- if 'desc' in test:
- mochi_desc = '<!-- Testing: %s -->\n' % test['desc']
- else:
- mochi_desc = ''
-
- if 'deferTest' in mochi_code:
- mochi_setup = ''
- mochi_footer = ''
- else:
- mochi_setup = ''
- mochi_footer = 'SimpleTest.finish();\n'
-
- for f in ['isPixel', 'todo_isPixel', 'deferTest', 'wrapFunction']:
- if f in mochi_code:
- mochi_setup += templates['mochitest.%s' % f]
- else:
- if not W3CMODE:
- print "Skipping mochitest for %s" % name
- mochi_name = ''
- mochi_desc = ''
- mochi_code = ''
- mochi_setup = ''
- mochi_footer = ''
-
- expectation_html = ''
- if 'expected' in test and test['expected'] is not None:
- expected = test['expected']
- expected_img = None
- if expected == 'green':
- expected_img = make_flat_image('green-100x50.png', 100, 50, 0,1,0,1)
- if W3CMODE: expected_img = "/images/" + expected_img
- elif expected == 'clear':
- expected_img = make_flat_image('clear-100x50.png', 100, 50, 0,0,0,0)
- if W3CMODE: expected_img = "/images/" + expected_img
- else:
- if ';' in expected: print "Found semicolon in %s" % name
- expected = re.sub(r'^size (\d+) (\d+)',
- r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)\ncr = cairo.Context(surface)',
- expected)
-
- if mapped_name.endswith("-manual"):
- png_name = mapped_name[:-len("-manual")]
- else:
- png_name = mapped_name
- expected += "\nsurface.write_to_png('%s/%s.png')\n" % (IMAGEOUTPUTDIR, png_name)
- eval(compile(expected, '<test %s>' % test['name'], 'exec'), {}, {'cairo':cairo})
- expected_img = "%s.png" % name
-
- if expected_img:
- expectation_html = ('<p class="output expectedtext">Expected output:' +
- '<p><img src="%s" class="output expected" id="expected" alt="">' % (expected_img))
-
- canvas = test.get('canvas', 'width="100" height="50"')
-
- prev = tests[i-1]['name'] if i != 0 else 'index'
- next = tests[i+1]['name'] if i != len(tests)-1 else 'index'
-
- name_wrapped = name.replace('.', '.&#8203;') # (see https://bugzilla.mozilla.org/show_bug.cgi?id=376188)
-
- refs = ''.join('<li><a href="%s/canvas.html#testrefs.%s">%s</a>\n' % (SPECOUTPUTPATH, n,n) for n in test.get('testing', []))
- if not W3CMODE and 'mozilla' in test and 'bug' in test['mozilla']:
- refs += '<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=%d">Bugzilla</a>' % test['mozilla']['bug']
-
- notes = '<p class="notes">%s' % test['notes'] if 'notes' in test else ''
-
- scripts = ''
- for s in test.get('scripts', []):
- scripts += '<script src="%s"></script>\n' % (s)
-
- images = ''
- for i in test.get('images', []):
- id = i.split('/')[-1]
- if '/' not in i:
- used_images[i] = 1
- i = '../images/%s' % i
- images += '<img src="%s" id="%s" class="resource">\n' % (i,id)
- mochi_images = images.replace('../images/', 'image_')
- if W3CMODE: images = images.replace("../images/", "/images/")
-
- fonts = ''
- fonthack = ''
- for i in test.get('fonts', []):
- fonts += '@font-face {\n font-family: %s;\n src: url("/fonts/%s.ttf");\n}\n' % (i, i)
- # Browsers require the font to actually be used in the page
- if test.get('fonthack', 1):
- fonthack += '<span style="font-family: %s; position: absolute; visibility: hidden">A</span>\n' % i
- if fonts:
- fonts = '<style>\n%s</style>\n' % fonts
-
- fallback = test.get('fallback', '<p class="fallback">FAIL (fallback content)</p>')
-
- desc = test.get('desc', '')
- escaped_desc = simpleEscapeJS(desc)
- template_params = {
- 'name':name, 'name_wrapped':name_wrapped, 'backrefs':backref_html(name),
- 'mapped_name':mapped_name,
- 'desc':desc, 'escaped_desc':escaped_desc,
- 'prev':prev, 'next':next, 'refs':refs, 'notes':notes, 'images':images,
- 'fonts':fonts, 'fonthack':fonthack,
- 'canvas':canvas, 'expected':expectation_html, 'code':code, 'scripts':scripts,
- 'mochi_name':mochi_name, 'mochi_desc':mochi_desc, 'mochi_code':mochi_code,
- 'mochi_setup':mochi_setup, 'mochi_footer':mochi_footer, 'mochi_images':mochi_images,
- 'fallback':fallback
- }
-
- if W3CMODE:
- f = codecs.open('%s/%s.html' % (TESTOUTPUTDIR, mapped_name), 'w', 'utf-8')
- f.write(templates['w3c'] % template_params)
- else:
- f = codecs.open('%s/%s.html' % (TESTOUTPUTDIR, name), 'w', 'utf-8')
- f.write(templates['standalone'] % template_params)
-
- f = codecs.open('%s/framed.%s.html' % (TESTOUTPUTDIR, name), 'w', 'utf-8')
- f.write(templates['framed'] % template_params)
-
- f = codecs.open('%s/minimal.%s.html' % (TESTOUTPUTDIR, name), 'w', 'utf-8')
- f.write(templates['minimal'] % template_params)
-
- if mochitest:
- mochitests.append(name)
- f = codecs.open('%s/mochitests/test_%s.html' % (MISCOUTPUTDIR, name), 'w', 'utf-8')
- f.write(templates['mochitest'] % template_params)
-
-def write_mochitest_makefile():
- f = open('%s/mochitests/Makefile.in' % MISCOUTPUTDIR, 'w')
- f.write(templates['mochitest.Makefile'])
- files = ['test_%s.html' % n for n in mochitests] + ['image_%s' % n for n in used_images]
- chunksize = 100
- chunks = []
- for i in range(0, len(files), chunksize):
- chunk = files[i:i+chunksize]
- name = '_TEST_FILES_%d' % (i / chunksize)
- chunks.append(name)
- f.write('%s = \\\n' % name)
- for file in chunk: f.write('\t%s \\\n' % file)
- f.write('\t$(NULL)\n\n')
- f.write('# split up into groups to work around command-line length limits\n')
- for name in chunks:
- f.write('libs:: $(%s)\n\t$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)\n\n' % name)
-
-if not W3CMODE:
- for i in used_images:
- shutil.copyfile("../../images/%s" % i, "%s/mochitests/image_%s" % (MISCOUTPUTDIR, i))
- write_mochitest_makefile()
-
-print
-
-def write_index():
- f = open('%s/index.html' % TESTOUTPUTDIR, 'w')
- f.write(templates['index.w3c' if W3CMODE else 'index'] % { 'updated':time.strftime('%Y-%m-%d', time.gmtime()) })
- f.write('\n<ul class="testlist">\n')
- depth = 1
- for category in category_names:
- name = category[1:-1] or ''
- count = len(category_contents_all[category])
- new_depth = category.count('.')
- while new_depth < depth: f.write(' '*(depth-1) + '</ul>\n'); depth -= 1
- f.write(' '*depth + templates['index.w3c.category.item' if W3CMODE else 'index.category.item'] % (name or 'all', name, count, '' if count==1 else 's'))
- while new_depth+1 > depth: f.write(' '*depth + '<ul>\n'); depth += 1
- for item in category_contents_direct.get(category, []):
- f.write(' '*depth + '<li><a href="%s.html">%s</a>\n' % (item, item) )
- while 0 < depth: f.write(' '*(depth-1) + '</ul>\n'); depth -= 1
-
-def write_category_indexes():
- for category in category_names:
- name = (category[1:-1] or 'all')
-
- f = open('%s/index.%s.html' % (TESTOUTPUTDIR, name), 'w')
- f.write(templates['index.w3c.frame' if W3CMODE else 'index.frame'] % { 'backrefs':backref_html(name), 'category':name })
- for item in category_contents_all[category]:
- f.write(templates['index.w3c.frame.item' if W3CMODE else 'index.frame.item'] % item)
-
-def write_reportgen():
- f = open('%s/reportgen.html' % MISCOUTPUTDIR, 'w')
- items_text = ',\n'.join(('"%s"' % item) for item in category_contents_all['.'])
- f.write(templates['reportgen'] % {'items':items_text })
-
-def write_results():
- results = {}
- uas = []
- uastrings = {}
- for item in category_contents_all['.']: results[item] = {}
-
- f = open('%s/results.html' % MISCOUTPUTDIR, 'w')
- f.write(templates['results'])
-
- if not os.path.exists('results.yaml'):
- print "Can't find results.yaml"
- else:
- for resultset in yaml.load(open('results.yaml', "r").read()):
- #title = "%s (%s)" % (resultset['ua'], resultset['time'])
- title = resultset['name']
- #assert title not in uas # don't allow repetitions
- if title not in uas:
- uas.append(title)
- uastrings[title] = resultset['ua']
- else:
- assert uastrings[title] == resultset['ua']
- for r in resultset['results']:
- if r['id'] not in results:
- print 'Skipping results for removed test %s' % r['id']
- continue
- results[r['id']][title] = (
- r['status'].lower(),
- re.sub(r'%(..)', lambda m: chr(int(m.group(1), 16)),
- re.sub(r'%u(....)', lambda m: unichr(int(m.group(1), 16)),
- r['notes'])).encode('utf8')
- )
-
- passes = {}
- for ua in uas:
- f.write('<th title="%s">%s\n' % (uastrings[ua], ua))
- passes[ua] = 0
- for id in category_contents_all['.']:
- f.write('<tr><td><a href="#%s" id="%s">#</a> <a href="%s.html">%s</a>\n' % (id, id, id, id))
- for ua in uas:
- status, details = results[id].get(ua, ('', ''))
- f.write('<td class="r %s"><ul class="d">%s</ul>\n' % (status, details))
- if status == 'pass': passes[ua] += 1
- f.write('<tr><th>Passes\n')
- for ua in uas:
- f.write('<td>%.1f%%\n' % ((100.0 * passes[ua]) / len(category_contents_all['.'])))
- f.write('<tr><td>\n')
- for ua in uas:
- f.write('<td>%s\n' % ua)
- f.write('</table>\n')
-
-def getNodeText(node):
- t, offsets = '', []
-
- # Skip over any previous annotations we added
- if node.nodeType == node.ELEMENT_NODE and 'testrefs' in node.getAttribute('class').split(' '):
- return t, offsets
-
- if node.nodeType == node.TEXT_NODE:
- val = node.nodeValue
- val = val.replace(unichr(0xa0), ' ') # replace &nbsp;s
- t += val
- offsets += [ (node, len(node.nodeValue)) ]
- for n in node.childNodes:
- child_t, child_offsets = getNodeText(n)
- t += child_t
- offsets += child_offsets
- return t, offsets
-
-def htmlSerializer(element):
- element.normalize()
- rv = []
- specialtext = ['style', 'script', 'xmp', 'iframe', 'noembed', 'noframes', 'noscript']
- empty = ['area', 'base', 'basefont', 'bgsound', 'br', 'col', 'embed', 'frame',
- 'hr', 'img', 'input', 'link', 'meta', 'param', 'spacer', 'wbr']
-
- def serializeElement(element):
- if element.nodeType == Node.DOCUMENT_TYPE_NODE:
- rv.append("<!DOCTYPE %s>" % element.name)
- elif element.nodeType == Node.DOCUMENT_NODE:
- for child in element.childNodes:
- serializeElement(child)
- elif element.nodeType == Node.COMMENT_NODE:
- rv.append("<!--%s-->" % element.nodeValue)
- elif element.nodeType == Node.TEXT_NODE:
- unescaped = False
- n = element.parentNode
- while n is not None:
- if n.nodeName in specialtext:
- unescaped = True
- break
- n = n.parentNode
- if unescaped:
- rv.append(element.nodeValue)
- else:
- rv.append(escapeHTML(element.nodeValue))
- else:
- rv.append("<%s" % element.nodeName)
- if element.hasAttributes():
- for name, value in element.attributes.items():
- rv.append(' %s="%s"' % (name, escapeHTML(value)))
- rv.append(">")
- if element.nodeName not in empty:
- for child in element.childNodes:
- serializeElement(child)
- rv.append("</%s>" % element.nodeName)
- serializeElement(element)
- return '<!DOCTYPE html>\n' + ''.join(rv)
-
-def write_annotated_spec():
- # Load the stripped-down XHTMLised copy of the spec
- doc = xml.dom.minidom.parse(open('current-work-canvas.xhtml', 'r'))
-
- # Insert our new stylesheet
- n = doc.getElementsByTagName('head')[0].appendChild(doc.createElement('link'))
- n.setAttribute('rel', 'stylesheet')
- n.setAttribute('href', '../common/canvas-spec.css' if W3CMODE else '../spectest.css')
- n.setAttribute('type', 'text/css')
-
- spec_assertion_patterns = []
- for a in spec_assertions:
- # Warn about problems
- if a['id'] not in spec_refs:
- print "Unused spec statement %s" % a['id']
-
- pattern_text = a['text']
-
- if 'keyword' in a:
- # Explicit keyword override
- keyword = a['keyword']
- else:
- # Extract the marked keywords, and remove the markers
- keyword = 'none'
- for kw in ['must', 'should', 'required']:
- if ('*%s*' % kw) in pattern_text:
- keyword = kw
- pattern_text = pattern_text.replace('*%s*' % kw, kw)
- break
- # Make sure there wasn't >1 keyword
- for kw in ['must', 'should', 'required']:
- assert('*%s*' % kw not in pattern_text)
-
- # Convert the special pattern format into regexp syntax
- pattern_text = (pattern_text.
- # Escape relevant characters
- replace('*', r'\*').
- replace('+', r'\+').
- replace('.', r'\.').
- replace('(', r'\(').
- replace(')', r'\)').
- replace('[', r'\[').
- replace(']', r'\]').
- # Convert special sequences back into unescaped regexp code
- replace(' ', r'\s+').
- replace(r'<\.\.\.>', r'.+').
- replace('<^>', r'()').
- replace('<eol>', r'\s*?\n')
- )
- pattern = re.compile(pattern_text, re.S)
- spec_assertion_patterns.append( (a['id'], pattern, keyword, a.get('previously', None)) )
- matched_assertions = {}
-
- def process_element(e):
- if e.nodeType == e.ELEMENT_NODE and (e.getAttribute('class') == 'impl' or e.hasAttribute('data-component')):
- for c in e.childNodes:
- process_element(c)
- return
-
- t, offsets = getNodeText(e)
- for id, pattern, keyword, previously in spec_assertion_patterns:
- m = pattern.search(t)
- if m:
- # When the pattern-match isn't enough to uniquely identify a sentence,
- # allow explicit back-references to earlier paragraphs
- if previously:
- if len(previously) >= 3:
- n, text, exp = previously
- else:
- n, text = previously
- exp = True
- node = e
- while n and node.previousSibling:
- node = node.previousSibling
- n -= 1
- if (text not in getNodeText(node)[0]) == exp:
- continue # discard this match
-
- if id in matched_assertions:
- print "Spec statement %s matches multiple places" % id
- matched_assertions[id] = True
-
- if m.lastindex != 1:
- print "Spec statement %s has incorrect number of match groups" % id
-
- end = m.end(1)
- end_node = None
- for end_node, o in offsets:
- if end < o:
- break
- end -= o
- assert(end_node)
-
- n1 = doc.createElement('span')
- n1.setAttribute('class', 'testrefs kw-%s' % keyword)
- n1.setAttribute('id', 'testrefs.%s' % id)
- n1.appendChild(doc.createTextNode(' '))
-
- n = n1.appendChild(doc.createElement('a'))
- n.setAttribute('href', '#testrefs.%s' % id)
- n.setAttribute('title', id)
- n.appendChild(doc.createTextNode('#'))
-
- n1.appendChild(doc.createTextNode(' '))
- for test_id in spec_refs.get(id, []):
- n = n1.appendChild(doc.createElement('a'))
- n.setAttribute('href', '../canvas/%s.html' % test_id)
- n.appendChild(doc.createTextNode(test_id))
- n1.appendChild(doc.createTextNode(' '))
- n0 = doc.createTextNode(end_node.nodeValue[:end])
- n2 = doc.createTextNode(end_node.nodeValue[end:])
-
- p = end_node.parentNode
- p.replaceChild(n2, end_node)
- p.insertBefore(n1, n2)
- p.insertBefore(n0, n1)
-
- t, offsets = getNodeText(e)
-
- for e in doc.getElementsByTagName('body')[0].childNodes:
- process_element(e)
-
- for s in spec_assertions:
- if s['id'] not in matched_assertions:
- print "Annotation incomplete: Unmatched spec statement %s" % s['id']
-
- # Convert from XHTML back to HTML
- doc.documentElement.removeAttribute('xmlns')
- doc.documentElement.setAttribute('lang', doc.documentElement.getAttribute('xml:lang'))
-
- head = doc.documentElement.getElementsByTagName('head')[0]
- head.insertBefore(doc.createElement('meta'), head.firstChild).setAttribute('charset', 'UTF-8')
-
- f = codecs.open('%s/canvas.html' % SPECOUTPUTDIR, 'w', 'utf-8')
- f.write(htmlSerializer(doc))
-
-if not W3CMODE:
- write_index()
- write_category_indexes()
- write_reportgen()
- write_results()
- write_annotated_spec()