1 /* 2 * Copyright Lodovico Giaretta 2016 - . 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 8 module test; 9 10 import std.experimental.xml; 11 12 import std.encoding: transcode, Latin1String; 13 import std.file: read, readText; 14 import std.functional: toDelegate; 15 import std.path; 16 import std.stdio: write, writeln; 17 import std.utf: UTFException; 18 19 auto indexes = 20 [ 21 "tests/sun/sun-valid.xml", 22 "tests/sun/sun-error.xml", 23 "tests/sun/sun-invalid.xml", 24 "tests/sun/sun-not-wf.xml", 25 "tests/xmltest/xmltest.xml", 26 "tests/oasis/oasis.xml", 27 "tests/ibm/ibm_oasis_invalid.xml", 28 "tests/ibm/ibm_oasis_not-wf.xml", 29 "tests/ibm/ibm_oasis_valid.xml", 30 "tests/ibm/xml-1.1/ibm_invalid.xml", 31 "tests/ibm/xml-1.1/ibm_not-wf.xml", 32 "tests/ibm/xml-1.1/ibm_valid.xml", 33 "tests/eduni/errata-2e/errata2e.xml", 34 "tests/eduni/xml-1.1/xml11.xml", 35 "tests/eduni/namespaces/1.0/rmt-ns10.xml", 36 "tests/eduni/namespaces/1.1/rmt-ns11.xml", 37 "tests/eduni/errata-3e/errata3e.xml", 38 "tests/eduni/namespaces/errata-1e/errata1e.xml", 39 "tests/eduni/errata-4e/errata4e.xml", 40 "tests/eduni/misc/ht-bh.xml" 41 ]; 42 43 struct Results 44 { 45 int[string] totals; 46 int[string] wrong; 47 48 static Results opCall() 49 { 50 Results result; 51 result.totals = ["valid": 0, "invalid": 0, "not-wf": 0, "error": 0, "skipped": 0]; 52 result.wrong = ["valid": 0, "invalid": 0, "not-wf": 0, "error": 0]; 53 return result; 54 } 55 56 void opOpAssign(string op)(Results other) 57 { 58 static if (op == "+") 59 { 60 foreach (key, val; other.totals) 61 totals[key] += val; 62 foreach (key, val; other.wrong) 63 wrong[key] += val; 64 } 65 else 66 { 67 static assert(0); 68 } 69 } 70 } 71 72 void writeIndent(int depth) 73 { 74 for (int i = 0; i < depth; i++) 75 write("\t"); 76 } 77 78 void printResults(Results results, int depth) 79 { 80 writeIndent(depth); 81 writeln("== RESULTS =="); 82 writeIndent(depth); 83 writeln(results.wrong["valid"], " valid inputs rejected out of ", results.totals["valid"], " total."); 84 writeIndent(depth); 85 writeln(results.wrong["invalid"], " invalid inputs accepted out of ", results.totals["invalid"], " total."); 86 writeIndent(depth); 87 writeln(results.wrong["not-wf"], " ill-formed inputs accepted out of ", results.totals["not-wf"], " total."); 88 writeIndent(depth); 89 writeln(results.wrong["error"], " erroneous inputs accepted out of ", results.totals["error"], " total."); 90 writeIndent(depth); 91 writeln(results.totals["skipped"], " inputs skipped because of unsupported features."); 92 } 93 94 Results handleTestcases(T)(string directory, ref T cursor, int depth) 95 { 96 auto results = Results(); 97 do 98 { 99 if (cursor.getName() == "TESTCASES") 100 { 101 writeIndent(depth); 102 write("TESTCASES"); 103 foreach (att; cursor.getAttributes()) 104 if (att.name == "PROFILE") 105 write(" -- ", att.value); 106 writeln(); 107 108 if (cursor.enter()) 109 { 110 results += handleTestcases(directory, cursor, depth + 1); 111 cursor.exit(); 112 } 113 } 114 else if (cursor.getName() == "TEST") 115 { 116 results += handleTest(directory, cursor, depth); 117 } 118 } 119 while (cursor.next()); 120 printResults(results, depth); 121 writeln(); 122 return results; 123 } 124 125 Results handleTest(T)(string directory, ref T cursor, int depth) 126 { 127 auto result = Results(); 128 129 string file, kind; 130 foreach (att; cursor.getAttributes()) 131 if (att.name == "ENTITIES" && att.value != "none") 132 { 133 result.totals["skipped"]++; 134 return result; 135 } 136 else if (att.name == "TYPE" && att.value in result.totals) 137 kind = att.value; 138 else if (att.name == "URI") 139 file = att.value; 140 141 result.totals[kind]++; 142 143 bool passed = true; 144 Throwable error; 145 try 146 { 147 parseFile(directory ~ dirSeparator ~ file); 148 } 149 catch (Throwable err) 150 { 151 passed = false; 152 error = err; 153 } 154 if (passed && kind != "valid") 155 { 156 writeIndent(depth); 157 write("FAILED: accepted "); 158 result.wrong[kind]++; 159 switch (kind) 160 { 161 case "invalid": 162 write("invalid "); 163 break; 164 case "not-wf": 165 write("ill-formed "); 166 break; 167 case "error": 168 write("erroneous "); 169 break; 170 default: 171 assert(0); 172 } 173 writeln("file ", file); 174 } 175 else if (!passed && kind == "valid") 176 { 177 writeIndent(depth); 178 result.wrong["valid"]++; 179 writeln("FAILED: rejected valid file ", file); 180 } 181 else 182 { 183 writeIndent(depth); 184 writeln("OK: ", file); 185 } 186 187 return result; 188 } 189 190 // callback used to ignore missing xml declaration, while throwing on invalid attributes 191 void uselessCallback(CursorError err) 192 { 193 if (err != CursorError.MISSING_XML_DECLARATION) 194 assert(0); 195 } 196 197 /++ 198 + Most tests are currently not working for the following reasons: 199 + - We don't have any validation, so we accept all files that seem well formed; 200 +/ 201 void main() 202 { 203 auto cursor = 204 chooseLexer!string 205 .parse 206 .cursor(&uselessCallback); // If an index is not well-formed, just tell us but continue parsing 207 208 auto results = Results(); 209 foreach (i, index; indexes) 210 { 211 writeln(i, " -- ", index); 212 213 cursor.setSource(readText(index)); 214 cursor.enter(); 215 216 results += handleTestcases(dirName(index), cursor, 1); 217 } 218 219 printResults(results, 0); 220 writeln(); 221 } 222 223 void parseFile(string filename) 224 { 225 void inspectOneLevel(T)(ref T cursor) 226 { 227 do 228 { 229 if (cursor.enter) 230 { 231 inspectOneLevel(cursor); 232 cursor.exit(); 233 } 234 } 235 while (cursor.next()); 236 } 237 238 string text; 239 try 240 { 241 text = readText(filename); 242 } 243 catch (UTFException) 244 { 245 auto raw = read(filename); 246 transcode(cast(Latin1String)raw, text); 247 } 248 249 auto cursor = 250 chooseParser!text(() { throw new Exception("AAAAHHHHH"); }) 251 .cursor(&uselessCallback); // lots of tests do not have an xml declaration 252 253 cursor.setSource(text); 254 inspectOneLevel(cursor); 255 }