|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /** |
|
7 * A test suite that runs WHATWG HTML parser tests. |
|
8 * The tests are from html5lib. |
|
9 * |
|
10 * http://html5lib.googlecode.com/ |
|
11 */ |
|
12 |
|
13 /** |
|
14 * A few utility functions. |
|
15 */ |
|
16 function log(entry) { |
|
17 |
|
18 } |
|
19 |
|
20 function startsWith(s, s2) { |
|
21 return s.indexOf(s2)==0; |
|
22 } |
|
23 |
|
24 function trimString(s) { |
|
25 return(s.replace(/^\s+/,'').replace(/\s+$/,'')); |
|
26 } |
|
27 |
|
28 /** |
|
29 * Parses an individual testcase into an array containing the input |
|
30 * string, a string representing the expected tree (DOM), and a list |
|
31 * of error messages. |
|
32 * |
|
33 * @param A string containing a single testcase |
|
34 */ |
|
35 function parseTestcase(testcase) { |
|
36 var lines = testcase.split("\n"); |
|
37 |
|
38 /* check that the first non-empty, non-comment line is #data */ |
|
39 for each (var line in lines) { |
|
40 if (!line || startsWith(line, "##")) { |
|
41 continue; |
|
42 } |
|
43 if (line == "#data") |
|
44 break; |
|
45 log(lines); |
|
46 throw "Unknown test format." |
|
47 } |
|
48 |
|
49 var input = []; |
|
50 var output = []; |
|
51 var errors = []; |
|
52 var fragment = []; |
|
53 var currentList = input; |
|
54 for each (var line in lines) { |
|
55 if (startsWith(line, "##todo")) { |
|
56 todo(false, line.substring(6)); |
|
57 continue; |
|
58 } |
|
59 if (!(startsWith(line, "#error") || |
|
60 startsWith(line, "#document") || |
|
61 startsWith(line, "#document-fragment") || |
|
62 startsWith(line, "#data"))) { |
|
63 currentList.push(line); |
|
64 } else if (line == "#errors") { |
|
65 currentList = errors; |
|
66 } else if (line == "#document") { |
|
67 currentList = output; |
|
68 } else if (line == "#document-fragment") { |
|
69 currentList = fragment; |
|
70 } |
|
71 } |
|
72 while (!output[output.length - 1]) { |
|
73 output.pop(); // zap trailing blank lines |
|
74 } |
|
75 //logger.log(input.length, output.length, errors.length); |
|
76 return [input.join("\n"), output.join("\n"), errors, fragment[0]]; |
|
77 } |
|
78 |
|
79 /** |
|
80 * A generator function that accepts a list of strings. Each list |
|
81 * member corresponds to the contents of a ".dat" file from the |
|
82 * html5lib test suite. |
|
83 * |
|
84 * @param The list of strings |
|
85 */ |
|
86 function test_parser(testlist) { |
|
87 for each (var testgroup in testlist) { |
|
88 var tests = testgroup.split("#data\n"); |
|
89 tests = ["#data\n" + test for each(test in tests) if (test)]; |
|
90 for each (var test in tests) { |
|
91 yield parseTestcase(test); |
|
92 } |
|
93 } |
|
94 } |
|
95 |
|
96 /** |
|
97 * Transforms a DOM document to a string matching the format in |
|
98 * the test cases. |
|
99 * |
|
100 * @param the DOM document |
|
101 */ |
|
102 function docToTestOutput(doc) { |
|
103 var walker = doc.createTreeWalker(doc, NodeFilter.SHOW_ALL, null); |
|
104 return addLevels(walker, "", "| ").slice(0,-1); // remove the last newline |
|
105 } |
|
106 |
|
107 /** |
|
108 * Creates a walker for a fragment that skips over the root node. |
|
109 * |
|
110 * @param an element |
|
111 */ |
|
112 function createFragmentWalker(elt) { |
|
113 return elt.ownerDocument.createTreeWalker(elt, NodeFilter.SHOW_ALL, |
|
114 function (node) { |
|
115 return elt == node ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT; |
|
116 }); |
|
117 } |
|
118 |
|
119 /** |
|
120 * Transforms the descendants of an element to a string matching the format |
|
121 * in the test cases. |
|
122 * |
|
123 * @param an element |
|
124 */ |
|
125 function fragmentToTestOutput(elt) { |
|
126 var walker = createFragmentWalker(elt); |
|
127 return addLevels(walker, "", "| ").slice(0,-1); // remove the last newline |
|
128 } |
|
129 |
|
130 function addLevels(walker, buf, indent) { |
|
131 if(walker.firstChild()) { |
|
132 do { |
|
133 buf += indent; |
|
134 switch (walker.currentNode.nodeType) { |
|
135 case Node.ELEMENT_NODE: |
|
136 buf += "<" |
|
137 var ns = walker.currentNode.namespaceURI; |
|
138 if ("http://www.w3.org/1998/Math/MathML" == ns) { |
|
139 buf += "math "; |
|
140 } else if ("http://www.w3.org/2000/svg" == ns) { |
|
141 buf += "svg "; |
|
142 } else if ("http://www.w3.org/1999/xhtml" != ns) { |
|
143 buf += "otherns "; |
|
144 } |
|
145 buf += walker.currentNode.localName + ">"; |
|
146 if (walker.currentNode.hasAttributes()) { |
|
147 var valuesByName = {}; |
|
148 var attrs = walker.currentNode.attributes; |
|
149 for (var i = 0; i < attrs.length; ++i) { |
|
150 var localName = attrs[i].localName; |
|
151 var name; |
|
152 var attrNs = attrs[i].namespaceURI; |
|
153 if (null == attrNs) { |
|
154 name = localName; |
|
155 } else if ("http://www.w3.org/XML/1998/namespace" == attrNs) { |
|
156 name = "xml " + localName; |
|
157 } else if ("http://www.w3.org/1999/xlink" == attrNs) { |
|
158 name = "xlink " + localName; |
|
159 } else if ("http://www.w3.org/2000/xmlns/" == attrNs) { |
|
160 name = "xmlns " + localName; |
|
161 } else { |
|
162 name = "otherns " + localName; |
|
163 } |
|
164 valuesByName[name] = attrs[i].value; |
|
165 } |
|
166 var keys = Object.keys(valuesByName).sort(); |
|
167 for (var i = 0; i < keys.length; ++i) { |
|
168 buf += "\n" + indent + " " + keys[i] + |
|
169 "=\"" + valuesByName[keys[i]] +"\""; |
|
170 } |
|
171 } |
|
172 break; |
|
173 case Node.DOCUMENT_TYPE_NODE: |
|
174 buf += "<!DOCTYPE " + walker.currentNode.name; |
|
175 if (walker.currentNode.publicId || walker.currentNode.systemId) { |
|
176 buf += " \""; |
|
177 buf += walker.currentNode.publicId; |
|
178 buf += "\" \""; |
|
179 buf += walker.currentNode.systemId; |
|
180 buf += "\""; |
|
181 } |
|
182 buf += ">"; |
|
183 break; |
|
184 case Node.COMMENT_NODE: |
|
185 buf += "<!-- " + walker.currentNode.nodeValue + " -->"; |
|
186 break; |
|
187 case Node.TEXT_NODE: |
|
188 buf += "\"" + walker.currentNode.nodeValue + "\""; |
|
189 break; |
|
190 } |
|
191 buf += "\n"; |
|
192 // In the case of template elements, children do not get inserted as |
|
193 // children of the template element, instead they are inserted |
|
194 // as children of the template content (which is a document fragment). |
|
195 if (walker.currentNode instanceof HTMLTemplateElement) { |
|
196 buf += indent + " content\n"; |
|
197 // Walk through the template content. |
|
198 var templateWalker = createFragmentWalker(walker.currentNode.content); |
|
199 buf = addLevels(templateWalker, buf, indent + " "); |
|
200 } |
|
201 buf = addLevels(walker, buf, indent + " "); |
|
202 } while(walker.nextSibling()); |
|
203 walker.parentNode(); |
|
204 } |
|
205 return buf; |
|
206 } |
|
207 |