0001: package gnu.xquery.testsuite;
0002:
0003: import java.io.*;
0004: import java.util.*;
0005: import gnu.lists.*;
0006: import gnu.text.*;
0007: import gnu.mapping.*;
0008: import gnu.expr.*;
0009: import gnu.xml.*;
0010: import gnu.kawa.xml.*;
0011: import gnu.mapping.Symbol;
0012: import gnu.xquery.lang.*;
0013: import org.xml.sax.helpers.AttributesImpl;
0014: import gnu.xquery.util.NodeUtils;
0015:
0016: /** Run a suite of XQuery tests, as read from an xml file. */
0017:
0018: public class RunXQTS extends FilterConsumer {
0019: static XQuery xqueryLanguage = XQuery.getInstance();
0020:
0021: /* #ifdef JAVA5 */
0022: // HashMap<String,String> expectedFailures = new HashMap<String,String>();
0023: // HashMap<String,String> modules = new HashMap<String,String>();
0024: // HashMap<String,Object> sources = new HashMap<String,Object>();
0025: // Stack<Symbol> externalVariablesSet = new Stack<Symbol>();
0026: // Stack<String> outputFileAlts = new Stack<String>();
0027: // Stack<String> outputCompareAlts = new Stack<String>();
0028: /* #else */
0029: Hashtable expectedFailures = new Hashtable();
0030: Hashtable modules = new Hashtable();
0031: Hashtable sources = new Hashtable();
0032: Stack externalVariablesSet = new Stack();
0033: Stack outputFileAlts = new Stack();
0034: Stack outputCompareAlts = new Stack();
0035: /* #endif */
0036: ModuleManager manager = ModuleManager.getInstance();
0037: Object failExpected;
0038:
0039: boolean verbose = true;
0040: boolean useComments = true;
0041:
0042: String directory;
0043: String catalog;
0044: String XQTSVersion;
0045: String ResultOffsetPath;
0046: String XQueryQueryOffsetPath;
0047: String XQueryXQueryOffsetPath;
0048: String XQueryFileExtension;
0049: String XQueryXFileExtension;
0050: Object contextItem;
0051:
0052: int passCount;
0053: int xpassCount;
0054: int failCount;
0055: int xfailCount;
0056: int cannotTellCount;
0057:
0058: /** Set of expected error codes. The format is "|Error1|..|ErrorN|". */
0059: StringBuffer expectedErrorsBuf = new StringBuffer("|");
0060: /** Same as expectedErrorBuf.toString() after collecting expected-errors. */
0061: String expectedErrors;
0062:
0063: String logFileName = "XQTS.log";
0064: XMLPrinter xqlog;
0065:
0066: String collectionID;
0067: Values collectionDocuments;
0068:
0069: private void summaryReport(int count, String label) {
0070: if (count > 0) {
0071: System.out.print(label);
0072: System.out.println(count);
0073: }
0074: }
0075:
0076: private void summaryReport() {
0077: summaryReport(passCount, "# of expected passes ");
0078: summaryReport(xfailCount, "# of expected failures ");
0079: summaryReport(xpassCount, "# of unexpected successes ");
0080: summaryReport(failCount, "# of unexpected failures ");
0081: summaryReport(cannotTellCount,
0082: "# of cannot-tell (Inspect) results ");
0083: }
0084:
0085: public static final String XQTS_RESULT_NAMESPACE = "http://www.w3.org/2005/02/query-test-XQTSResult";
0086:
0087: static Object testSuiteResultElementType;
0088: static {
0089: NamespaceBinding namespaceNodes = new NamespaceBinding(null,
0090: XQTS_RESULT_NAMESPACE, new NamespaceBinding("q",
0091: XQuery.QEXO_FUNCTION_NAMESPACE,
0092: NamespaceBinding.predefinedXML));
0093: Symbol sym = Symbol.make(XQTS_RESULT_NAMESPACE,
0094: "test-suite-result", "");
0095: testSuiteResultElementType = new XName(sym, namespaceNodes);
0096: }
0097: static Object testRunElementType = Symbol.make(
0098: XQTS_RESULT_NAMESPACE, "test-run", "");
0099: static Object testSuiteElementType = Symbol.make(
0100: XQTS_RESULT_NAMESPACE, "test-suite", "");
0101: static Object testCaseElementType = Symbol.make(
0102: XQTS_RESULT_NAMESPACE, "test-case", "");
0103:
0104: private void writeStartElement(String name) {
0105: xqlog
0106: .startElement(Symbol.make(XQTS_RESULT_NAMESPACE, name,
0107: ""));
0108: }
0109:
0110: private void writeStartAttribute(String name) {
0111: xqlog.startAttribute(name);
0112: }
0113:
0114: private void writeAttribute(String name, String value) {
0115: writeStartAttribute(name);
0116: xqlog.write(value);
0117: xqlog.endAttribute();
0118: }
0119:
0120: private void writeQexoAttribute(String name, String value) {
0121: xqlog.startAttribute(Symbol.make(
0122: XQuery.QEXO_FUNCTION_NAMESPACE, name, "q"));
0123: xqlog.write(value);
0124: xqlog.endAttribute();
0125: }
0126:
0127: private void writeVerbose(String name, String value) {
0128: if (useComments) {
0129: // The tricky part is to make sure that the result can be validated.
0130: // Specifically, no spaces are allowed in a <test-case>.
0131: xqlog.printIndent = -1;
0132: xqlog.beginComment();
0133: xqlog.printIndent = 0;
0134: xqlog.writeBreakFill();
0135: xqlog.write(name);
0136: xqlog.write(": ");
0137: xqlog.write(value);
0138: xqlog.writeBreakFill();
0139: xqlog.endComment();
0140: } else
0141: writeQexoAttribute(name, value);
0142: }
0143:
0144: public static void main(String[] args) {
0145: gnu.xquery.lang.XQuery.registerEnvironment();
0146: Language.requirePedantic = true;
0147: for (int i = 0; i < args.length; i++) {
0148: try {
0149: RunXQTS runner = new RunXQTS(new CharArrayOutPort());
0150: runner.directory = args[i];
0151: runner.catalog = runner.directory + "/XQTSCatalog.xml";
0152: System.err.println("catalog: " + runner.catalog);
0153: XMLPrinter xqlog = new XMLPrinter(
0154: new BufferedOutputStream(new FileOutputStream(
0155: runner.logFileName)), FilePath
0156: .valueOf(runner.logFileName));
0157: runner.xqlog = xqlog;
0158: xqlog.setPrintXMLdecl(true);
0159: xqlog.setStyle("xml");
0160: xqlog.useEmptyElementTag = 1;
0161: Object saveIndent = XMLPrinter.indentLoc.get(null);
0162: XMLPrinter.indentLoc.set("pretty");
0163: xqlog.startDocument();
0164: XMLPrinter.indentLoc.set(saveIndent);
0165:
0166: Document.parse(runner.catalog, runner);
0167: xqlog.endDocument();
0168: runner.summaryReport();
0169: xqlog.close();
0170: } catch (Throwable ex) {
0171: ex.printStackTrace();
0172: System.err.println("caught " + ex
0173: + " while processing " + args[i]);
0174: }
0175: }
0176: }
0177:
0178: int nesting = 0;
0179: Object currentElementType;
0180: Symbol currentElementSymbol;
0181: /* #ifdef JAVA5 */
0182: // Stack<Object> elementTypeStack = new Stack<Object>();
0183: /* #else */
0184: Stack elementTypeStack = new Stack();
0185: /* #endif */
0186: boolean inStartTag;
0187: int attrValueStart;
0188: // Start in cout's buffer of current element, indexed by nesting level.
0189: int[] elementStartIndex = new int[20];
0190: AttributesImpl attributes = new AttributesImpl();
0191:
0192: String query = null;
0193: String expect = null;
0194:
0195: CharArrayOutPort cout;
0196:
0197: public RunXQTS(CharArrayOutPort out) {
0198: super (out);
0199: this .cout = out;
0200:
0201: expectFailures("K-ReplaceFunc-8",
0202: "allow bad regex replacement string");
0203: expectFailures("K2-MatchesFunc-1", "allow bad regex pattern");
0204: expectFailures("static-context-1",
0205: "unchecked unknownType in element(*,TypeName)");
0206: expectFailures("fn-abs-more-args-023|fn-abs-more-args-024",
0207: "testsuite error (4023): -0 is not valid unsignedLong/unsignedShort literal");
0208: expectFailures("ST-WhereExpr001",
0209: "testsuite error (4024) - should not require error");
0210: expectFailures("K2-SeqExprCast-207",
0211: "was testsuite error(4252) - now mismatch between '>' and '>'");
0212: /* #ifndef JAVA5 */
0213: expectFailures(
0214: "surrogates12|surrogates13|surrogates14|surrogates15",
0215: "surrogates not handled by java.util.regex");
0216: /* #endif */
0217: expectFailures("K-SeqExprInstanceOf-53",
0218: "too lenient about non-stanadrd types: void");
0219: expectFailures(
0220: "ST-Axes001|ST-Axes002|ST-Axes003|ST-Axes004|ST-Axes005|"
0221: + "ST-Axes006|ST-Axes007|ST-Axes008|ST-Axes009|ST-Axes010|"
0222: + "ST-Axes011|ST-Axes012|ST-Axes013|ST-Axes014|ST-Axes015",
0223: "depends on static typing feature");
0224: expectFailures(
0225: "fn-id-dtd-5|fn-id-dtd-7|fn-id-dtd-8|fn-id-dtd-9|"
0226: + "fn-id-dtd-12|fn-id-dtd-13|fn-id-dtd-15|fn-id-dtd-16|"
0227: + "fn-id-dtd-17|fn-id-dtd-18|fn-id-dtd-19|"
0228: + "fn-id-dtd-20|fn-id-dtd-21|fn-id-dtd-23|",
0229: "fn:id only works with xml:id so far");
0230: expectFailures("fn-idref-dtd-5|fn-idref-dtd-7|fn-idref-dtd-8|"
0231: + "fn-idref-dtd-9|fn-idref-dtd-12|fn-idref-dtd-13|"
0232: + "fn-idref-dtd-14|fn-idref-dtd-15|fn-idref-dtd-16|"
0233: + "fn-idref-dtd-17|fn-idref-dtd-18|fn-idref-dtd-19|"
0234: + "fn-idref-dtd-20|fn-idref-dtd-21|fn-idref-dtd-23|",
0235: "fn:idref doesn't do much yet");
0236: /* #ifndef use:java.text.Normalizer */
0237: expectFailures(
0238: "fn-normalize-unicode1args-1|"
0239: + "fn-normalize-unicode1args-2|"
0240: + "fn-normalize-unicode1args-3|"
0241: + "fn-normalize-unicode1args-4|"
0242: + "fn-normalize-unicode1args-5|"
0243: + "fn-normalize-unicode1args-6|"
0244: + "fn-normalize-unicode2args-1|"
0245: + "fn-normalize-unicode2args-2|"
0246: + "fn-normalize-unicode2args-3|"
0247: + "fn-normalize-unicode-1|"
0248: + "fn-normalize-unicode-3|fn-normalize-unicode-4|"
0249: + "fn-normalize-unicode-5|fn-normalize-unicode-6|"
0250: + "fn-normalize-unicode-7|K-NormalizeUnicodeFunc-4|"
0251: + "K-NormalizeUnicodeFunc-5|K-NormalizeUnicodeFunc-6|"
0252: + "K-NormalizeUnicodeFunc-7|K-NormalizeUnicodeFunc-8|"
0253: + "K-NormalizeUnicodeFunc-11|K-NormalizeUnicodeFunc-12",
0254: "fn:normalize-unicode not unimplemented yet");
0255: /* #endif */
0256: // RunXQTS failures rather than Qexo errors:
0257: // Some work under gcj but not JDK 1.4.x or 1.5.0_05:
0258: expectFailures(
0259: "vardeclerr|K-InternalVariablesWith-17|K-InternalVariablesWith-18",
0260: "missing check for circular definitions");
0261: expectFailures(
0262: "K-TimeAddDTD-1|K-TimeAddDTD-2|K-TimeSubtractDTD-1",
0263: "bad interaction between fields and millis");
0264: expectFailures("op-time-greater-than-2",
0265: "comparing xs:time doesn't handle differing timezones");
0266: expectFailures(
0267: "K-SubstringBeforeFunc-5|K-SubstringAfterFunc-5|"
0268: + "K-ContainsFunc-5|K-StartsWithFunc-5|K-EndsWithFunc-5",
0269: "some string functions don't support collation argument");
0270: expectFailures("caselessmatch04", "regex/unicode special case");
0271: expectFailures(
0272: "string-queries-results-q4|K2-FunctionProlog-7|K2-FunctionProlog-17|K2-FunctionProlog-18|K2-FunctionProlog-19|K2-FunctionProlog-22",
0273: "function conversion incorrect for user-defined functions");
0274: expectFailures("caselessmatch10|caselessmatch11",
0275: // Need to translate [xxx-[yyy]] to [xxx&&[^yyy]].
0276: "regex range subtraction not implemented");
0277: }
0278:
0279: private void expectFailures(String testNames, String reason) {
0280: while (testNames != null) {
0281: int dot = testNames.indexOf('|');
0282: String testName;
0283: if (dot >= 0) {
0284: testName = testNames.substring(0, dot);
0285: testNames = testNames.substring(dot + 1);
0286: } else {
0287: testName = testNames;
0288: testNames = null;
0289: }
0290: if (testName.length() > 0)
0291: expectedFailures.put(testName, reason);
0292: }
0293: }
0294:
0295: public void startElement(Object type) {
0296: if (inStartTag)
0297: handleStartTag();
0298: attributes.clear();
0299: inStartTag = true;
0300: elementTypeStack.push(currentElementType);
0301: currentElementType = type;
0302: currentElementSymbol = type instanceof Symbol ? (Symbol) type
0303: : null;
0304: /*
0305: System.err.println("startElement "+typeName);
0306: if ("test-suite".equals(typeName) && nesting == 0)
0307: inTestSuite = true;
0308: else if ("test-element".equals(typeName))
0309: {
0310: }
0311: else if ("test".equals(typeName)
0312: && (nesting == 0 || (inTestSuite && nesting == 1)))
0313: inTest = true;
0314: else if (inTestSuite ? nesting == 2 : nesting == 1)
0315: {
0316: cout.setLength(0);
0317: currentTag = typeName;
0318: }
0319: else if (currentTag == null)
0320: throw new RuntimeException("saw <"+typeName+"> not in <test>");
0321: else
0322: base.startElement(type);
0323: */
0324: nesting++;
0325: }
0326:
0327: boolean tagMatches(String localName) {
0328: if (localName.equals(currentElementSymbol.getLocalName()))
0329: // also check uri FIXME
0330: return true;
0331: return false;
0332: }
0333:
0334: public void handleStartTag() {
0335: elementStartIndex[nesting] = cout.length();
0336: if (tagMatches("test-suite")) {
0337: XQueryQueryOffsetPath = attributes
0338: .getValue("XQueryQueryOffsetPath");
0339: XQueryXQueryOffsetPath = attributes
0340: .getValue("XQueryXQueryOffsetPath");
0341: XQueryFileExtension = attributes
0342: .getValue("XQueryFileExtension");
0343: XQueryXFileExtension = attributes
0344: .getValue("XQueryXFileExtension");
0345: ResultOffsetPath = attributes.getValue("ResultOffsetPath");
0346: XQTSVersion = attributes.getValue("version");
0347:
0348: xqlog.startElement(testSuiteResultElementType);
0349: writeStartElement("implementation");
0350: writeAttribute("name", "Qexo");
0351: writeAttribute("version", kawa.Version.getVersion());
0352: writeStartElement("organization");
0353: writeAttribute("name", "GNU / Per Bothner");
0354: xqlog.endElement();
0355: writeStartElement("submittor");
0356: String user = System.getProperty("user.name");
0357: if ("bothner".equals(user)) {
0358: writeAttribute("name", "Per Bothner");
0359: writeAttribute("email", "per@bothner.com");
0360: } else
0361: writeAttribute("name", user);
0362: xqlog.endElement();
0363: xqlog.endElement();
0364: writeStartElement("syntax");
0365: xqlog.write("XQuery");
0366: xqlog.endElement();
0367: xqlog.startElement(testRunElementType);
0368: StringBuffer sbuf = new StringBuffer();
0369: gnu.kawa.xml.XTimeType.dateTimeType.now()
0370: .toStringDate(sbuf);
0371: writeAttribute("dateRun", sbuf.toString());
0372: xqlog.startElement(testSuiteElementType);
0373: writeAttribute("version", XQTSVersion);
0374: xqlog.endElement();
0375: xqlog.endElement();
0376:
0377: } else if (tagMatches("test-element")) {
0378: xqlog.writeComment("test-element "
0379: + attributes.getValue("name"));
0380: } else if (tagMatches("test-case")) {
0381: testName = attributes.getValue("name");
0382: scenario = attributes.getValue("scenario");
0383: testFilePath = attributes.getValue("FilePath");
0384: testQueryName = null;
0385: outputFileAlts.clear();
0386: outputCompareAlts.clear();
0387: expectedErrorsBuf.setLength(1);
0388: manager.clear();
0389: } else if (tagMatches("query")) {
0390: testQueryName = attributes.getValue("name");
0391: } else if (tagMatches("source")) {
0392: String ID = attributes.getValue("ID");
0393: String filename = attributes.getValue("FileName");
0394: sources.put(ID, filename);
0395: } else if (testName == null && tagMatches("module")) {
0396: String ID = attributes.getValue("ID");
0397: String filename = attributes.getValue("FileName");
0398: modules.put(ID, filename);
0399: } else if (tagMatches("collection")) {
0400: collectionID = attributes.getValue("ID");
0401: collectionDocuments = new Values();
0402: sources.put(collectionID, collectionDocuments);
0403: }
0404: inStartTag = false;
0405: }
0406:
0407: String testName;
0408: String scenario;
0409: String testQueryName;
0410: String testFilePath;
0411: String testQuery;
0412:
0413: int maxTests = -1;
0414:
0415: void report(String result, String comment) {
0416: boolean failed = "fail".equals(result);
0417: if (failExpected == null) {
0418: if (failed) {
0419: System.out.println("FAIL: " + testName);
0420: failCount++;
0421: } else if ("cannot tell".equals(result))
0422: cannotTellCount++;
0423: else
0424: passCount++;
0425: } else {
0426: if (failed)
0427: xfailCount++;
0428: else {
0429: System.out.println("XPASS: " + testName);
0430: xpassCount++;
0431: }
0432: }
0433:
0434: writeAttribute("result", result);
0435:
0436: if (failed && failExpected != null) {
0437: StringBuffer sbuf = new StringBuffer("(expected-to-fail: ");
0438: sbuf.append(failExpected.toString());
0439: sbuf.append(')');
0440: if (comment != null) {
0441: sbuf.append("; ");
0442: sbuf.append(comment);
0443: }
0444: comment = sbuf.toString();
0445: }
0446: if (comment != null)
0447: writeAttribute("comment", comment);
0448: }
0449:
0450: public void evalTest(String testName) throws Throwable {
0451: failExpected = expectedFailures.get(testName);
0452: if (failExpected == null) {
0453: // Check for a wildcard: replace a final non-negative integer by '*'.
0454: int len = testName.length();
0455: while (--len > 0
0456: && Character.digit(testName.charAt(len), 10) >= 0)
0457: ;
0458: failExpected = expectedFailures.get(testName.substring(0,
0459: len + 1) + '*');
0460: }
0461: Environment env = Environment.getCurrent();
0462: SourceMessages messages = new SourceMessages();
0463: String filename = directory + '/' + XQueryQueryOffsetPath
0464: + testFilePath + testQueryName + XQueryFileExtension;
0465: InPort in;
0466: expectedErrors = expectedErrorsBuf.toString();
0467: try {
0468: in = InPort.openFile(filename);
0469: } catch (java.io.FileNotFoundException ex) {
0470: String xfilename = directory + '/' + XQueryXQueryOffsetPath
0471: + testFilePath + testQueryName
0472: + XQueryXFileExtension;
0473: if (new java.io.File(xfilename).exists()) {
0474: report("fail", "xqueryx not implemented");
0475: return;
0476: }
0477: throw ex;
0478: }
0479: Compilation comp;
0480: Procedure withContextProc = null;
0481: try {
0482: if (contextItem != null) {
0483: withContextProc = xqueryLanguage.evalToFocusProc(in,
0484: messages);
0485: comp = null;
0486: } else
0487: comp = xqueryLanguage.parse(in, messages,
0488: Language.PARSE_IMMEDIATE);
0489: if (messages.seenErrors())
0490: throw new SyntaxException(messages);
0491: } catch (SyntaxException ex) {
0492: in.close();
0493: SourceError error = messages.getErrors();
0494: String errorString = error == null ? "" : "|" + error.code
0495: + "|";
0496: if (expectedErrors.indexOf(errorString) >= 0) {
0497: report("pass", null);
0498: } else if (errorString.equals("|XQST0009|")) {
0499:
0500: if (failExpected == null)
0501: failExpected = "'import schema' not implemented";
0502: report("fail", null);
0503: } else if (error.message != null
0504: && error.message
0505: .indexOf("unknown type xs:NOTATION") >= 0
0506: && (expectedErrors.equals("|XPST0080|") || expectedErrors
0507: .equals("|XPST0017|"))) {
0508: report("fail", null);
0509: } else if (error.message != null
0510: && error.message.indexOf("unknown type xs:ENTITY") >= 0
0511: && (expectedErrors.equals("|XPTY0004|"))) {
0512: report("fail", null);
0513: } else if (error.message != null
0514: && error.message.indexOf("unknown function") >= 0
0515: && (expectedErrors.equals("|XPDY0002|")
0516: || expectedErrors.equals("|XPTY0004|")
0517: || expectedErrors.equals("|XQDY0025|")
0518: || expectedErrors.equals("|FODC0001|") || (expectedErrors
0519: .equals("|XPST0017|") && (error.message
0520: .endsWith(" fn:id") || error.message
0521: .endsWith(" fn:idref")))))
0522:
0523: {
0524: report("fail", null);
0525: } else if (expectedErrors.length() > 1)
0526: report("pass", "static error: " + error + " expected:"
0527: + expectedErrors);
0528: else
0529: report("fail", "static error: " + error.message);
0530: return;
0531: }
0532: in.close();
0533:
0534: CallContext ctx = CallContext.getInstance();
0535: if (contextItem != null) {
0536: gnu.math.IntNum one = gnu.math.IntNum.one();
0537: withContextProc.check3(contextItem, one, one, ctx);
0538: }
0539: gnu.lists.Consumer save = ctx.consumer;
0540: CharArrayOutPort out = new CharArrayOutPort();
0541: XMLPrinter xout = new XMLPrinter(out, false);
0542: xout.strict = true;
0543: xout.useEmptyElementTag = 1;
0544: xout.escapeNonAscii = false;
0545: xout.canonicalizeCDATA = true;
0546: ctx.consumer = xout;
0547: try {
0548: if (contextItem != null)
0549: ctx.runUntilDone();
0550: else
0551: ModuleExp.evalModule(env, ctx, comp, null, null);
0552: } catch (Throwable ex) {
0553: if (ex instanceof NumberFormatException
0554: && expectedErrors.indexOf("|FORG0001|") >= 0)
0555: report("pass", "caught NumberFormatException expected:"
0556: + expectedErrors);
0557: else if (ex instanceof ClassCastException
0558: && (expectedErrors.indexOf("|XPTY0004|") >= 0
0559: || expectedErrors.indexOf("|XPTY0020|") >= 0
0560: || expectedErrors.indexOf("|FORG0001|") >= 0 || expectedErrors
0561: .indexOf("|FOAR0002|") >= 0))
0562: report("pass", "caught ClassCastException expected:"
0563: + expectedErrors);
0564: else if (expectedErrors.length() > 1)
0565: report("pass", "caught " + ex + " expected:"
0566: + expectedErrors);
0567: else {
0568: report("fail", "caught " + ex);
0569: if (verbose) {
0570: CharArrayWriter wr = new CharArrayWriter();
0571: PrintWriter pr = new PrintWriter(wr);
0572: ex.printStackTrace(pr);
0573: pr.flush();
0574: writeVerbose("stack", wr.toString());
0575: wr.close();
0576: }
0577: }
0578: return;
0579: }
0580:
0581: if (messages.seenErrors()) {
0582: if (expectedErrors.length() > 1)
0583: report("pass", "error: " + messages.getErrors()
0584: + " expected: " + expectedErrors);
0585: else
0586: report("fail", "error: " + messages.getErrors());
0587: return;
0588: }
0589:
0590: if ("trivial".equals(scenario)) {
0591: failExpected = "trivial embedding not implemented";
0592: report("fail", null);
0593: return;
0594: }
0595:
0596: String actual = new String(out.toCharArray());
0597: byte[] expectedBytes = new byte[1024];
0598: xout.close();
0599: ctx.consumer = save;
0600:
0601: int numOutputFileAlts = outputFileAlts.size();
0602: boolean foundMatchingOutput = false;
0603: boolean displayDifference = false;
0604: String expected = null;
0605: String compare = null;
0606: for (int ialt = 0; ialt < numOutputFileAlts; ialt++) {
0607: compare = (String) outputCompareAlts.elementAt(ialt);
0608: if ("Ignore".equals(compare)) {
0609: report("pass", null);
0610: foundMatchingOutput = true;
0611: break;
0612: }
0613: String outname = directory + '/' + ResultOffsetPath
0614: + testFilePath + outputFileAlts.elementAt(ialt);
0615: FileInputStream expectStream = new FileInputStream(outname);
0616: int expectedLength = 0;
0617: for (;;) {
0618: int avail = expectedBytes.length - expectedLength;
0619: if (avail < 1024) {
0620: byte[] tmp = new byte[2 * expectedBytes.length];
0621: System.arraycopy(expectedBytes, 0, tmp, 0,
0622: expectedLength);
0623: expectedBytes = tmp;
0624: }
0625: int n = expectStream.read(expectedBytes,
0626: expectedLength, avail);
0627: if (n < 0)
0628: break;
0629: expectedLength += n;
0630: }
0631: expectStream.close();
0632: expected = new String(expectedBytes, 0, expectedLength,
0633: "UTF-8");
0634: expected = expected.replaceAll("\r", "");
0635: actual = actual.replaceAll("\r", "");
0636: boolean matches = matches(actual, expected, compare);
0637: if (matches) {
0638: report("pass", null);
0639: foundMatchingOutput = true;
0640: break;
0641: } else if ("Inspect".equals(compare)) {
0642: report("cannot tell", null);
0643: foundMatchingOutput = true;
0644: displayDifference = verbose;
0645: break;
0646: }
0647: }
0648:
0649: if (!foundMatchingOutput) {
0650: if (expectedErrors.length() > 1) {
0651: report("fail", "expected error: " + expectedErrors);
0652: return;
0653: } else {
0654: report("fail", null);
0655: if (verbose && expectedFailures.get(testName) == null)
0656: displayDifference = true;
0657: }
0658: }
0659: if (displayDifference) { // This only displays a single expected result.
0660: writeVerbose("compare", compare);
0661: writeVerbose("expected", expected);
0662: writeVerbose("actual", actual);
0663: }
0664: }
0665:
0666: private static int grabAttribute(String str, int start) {
0667: char inAttr = 0;
0668: for (int i = start;;) {
0669: if (i >= str.length())
0670: return -1;
0671: char ch = str.charAt(i++);
0672: if (inAttr == 0 && (ch == '\"' || ch == '\''))
0673: inAttr = ch;
0674: else if (ch == inAttr)
0675: return i;
0676: }
0677: }
0678:
0679: public static boolean matches(String arg1 /* result */,
0680: String arg2 /*expected*/, String compare) {
0681: int len1 = arg1.length();
0682: int len2 = arg2.length();
0683: int i1 = 0, i2 = 0;
0684: boolean intag = false;
0685: int start_attr1 = 0;
0686: int start_attr2 = 0;
0687: boolean isXML = "XML".equals(compare)
0688: || "Fragment".equals(compare);
0689: char inAttr = 0;
0690: for (;;) {
0691: if (i1 == len1 && i2 == len2)
0692: return true;
0693: int c1 = i1 == len1 ? -1 : arg1.charAt(i1);
0694: int c2 = i2 == len2 ? -1 : arg2.charAt(i2);
0695: if ((c1 == '&' && arg1.charAt(i1 + 1) == '#' && i1 + 3 < len1)
0696: || (c2 == '&' && arg2.charAt(i2 + 1) == '#' && i2 + 3 < len2)) {
0697: try {
0698: if (c1 == '&') {
0699: int base1 = 10;
0700: i1 += 2;
0701: if (arg1.charAt(i1) == 'x') {
0702: i1++;
0703: base1 = 16;
0704: }
0705: int semi1 = arg1.indexOf(';', i1);
0706: c1 = Integer.parseInt(
0707: arg1.substring(i1, semi1), base1);
0708: i1 = semi1;
0709: } else if (c1 >= 0xD800 && c1 < 0xDC00
0710: && i1 + 1 < len1)
0711: c1 = (c1 - 0xD800) * 0x400
0712: + (arg1.charAt(++i1) - 0xDC00)
0713: + 0x10000;
0714: if (c2 == '&') {
0715: int base2 = 10;
0716: i2 += 2;
0717: if (arg2.charAt(i2) == 'x') {
0718: i2++;
0719: base2 = 16;
0720: }
0721: int semi2 = arg2.indexOf(';', i2);
0722: c2 = Integer.parseInt(
0723: arg2.substring(i2, semi2), base2);
0724: i2 = semi2;
0725: } else if (c2 >= 0xD800 && c2 < 0xDC00
0726: && i2 + 1 < len2)
0727: c2 = (c2 - 0xD800) * 0x400
0728: + (arg2.charAt(++i2) - 0xDC00)
0729: + 0x10000;
0730: } catch (Throwable ex) {
0731: return false;
0732: }
0733: if (c1 != c2)
0734: return false;
0735: i1++;
0736: i2++;
0737: } else if (c1 == c2) {
0738: if (c1 == '<' && isXML) {
0739: intag = true;
0740: start_attr1 = 0;
0741: start_attr2 = 0;
0742: inAttr = 0;
0743: } else if (intag && c1 == '>') {
0744: intag = false;
0745: } else if (intag && Character.isWhitespace((char) c1)
0746: && inAttr == 0)
0747:
0748: {
0749: start_attr1 = i1 + 1;
0750: start_attr2 = i2 + 1;
0751: ;
0752: } else if (intag && inAttr == 0
0753: && (c1 == '"' || c1 == '\'')) {
0754: inAttr = (char) c1;
0755: } else if (intag && inAttr == c1) {
0756: start_attr1 = 0;
0757: start_attr2 = 0;
0758: inAttr = 0;
0759: }
0760: i1++;
0761: i2++;
0762: } else if (intag && start_attr1 > 0) {
0763: i1 = start_attr1;
0764: i2 = start_attr2;
0765: /* #ifdef JAVA5 */
0766: // Stack<String> attrs1 = new Stack<String>();
0767: // Stack<String> attrs2 = new Stack<String>();
0768: /* #else */
0769: Stack attrs1 = new Stack();
0770: Stack attrs2 = new Stack();
0771: /* #endif */
0772: for (;;) {
0773: int end1 = grabAttribute(arg1, i1);
0774: int end2 = grabAttribute(arg2, i2);
0775: if (end1 < 0 || end2 < 0)
0776: return false;
0777: String attr1 = arg1.substring(i1, end1);
0778: attrs1.push(attr1);
0779: String attr2 = arg2.substring(i2, end2);
0780: attrs2.push(attr2);
0781: i1 = end1;
0782: i2 = end2;
0783: for (;;) {
0784: if (i1 >= len1)
0785: return false;
0786: c1 = arg1.charAt(i1++);
0787: if (!Character.isWhitespace((char) c1))
0788: break;
0789: }
0790: for (;;) {
0791: if (i2 >= len2)
0792: return false;
0793: c2 = arg2.charAt(i2++);
0794: if (!Character.isWhitespace((char) c2))
0795: break;
0796: }
0797: boolean done1 = c1 == '/' || c1 == '>';
0798: boolean done2 = c2 == '/' || c2 == '>';
0799: if (done1 && done2)
0800: break;
0801: if (done1 || done2)
0802: return false;
0803: i1--;
0804: i2--;
0805: }
0806: // Same number of attributes.
0807: // Do an O(n^2) search to make sure the sets are equal.
0808: for (int i = attrs1.size(); --i >= 0;) {
0809: String attr1 = (String) attrs1.elementAt(i);
0810: for (int j = attrs2.size();;) {
0811: if (--j < 0)
0812: return false;
0813: String attr2 = (String) attrs2.elementAt(j);
0814: if (attr1.equals(attr2))
0815: break;
0816: }
0817: }
0818: start_attr1 = 0;
0819: start_attr2 = 0;
0820: intag = false;
0821: } else if (isFloatChar(c1) ? (isFloatChar(c2) || (i2 > 0 && isFloatChar(arg2
0822: .charAt(i2 - 1))))
0823: : (isFloatChar(c2) && (i1 > 0 && isFloatChar(arg1
0824: .charAt(i1 - 1))))) {
0825: int start1 = i1, start2 = i2;
0826: while (start1 > 0
0827: && isFloatChar(arg1.charAt(start1 - 1)))
0828: start1--;
0829: while (start2 > 0
0830: && isFloatChar(arg2.charAt(start2 - 1)))
0831: start2--;
0832: int end1 = i1, end2 = i2;
0833: while (end1 < len1 && isFloatChar(arg1.charAt(end1)))
0834: end1++;
0835: while (end2 < len2 && isFloatChar(arg2.charAt(end2)))
0836: end2++;
0837: if (end1 <= start1 || end2 <= start2)
0838: return false;
0839: String word1 = arg1.substring(start1, end1);
0840: String word2 = arg2.substring(start2, end2);
0841: try {
0842: float f1 = Float.parseFloat(word1);
0843: float f2 = Float.parseFloat(word2);
0844: if (Float.floatToIntBits(f1) != Float
0845: .floatToIntBits(f2))
0846: return false;
0847: } catch (Throwable ex) {
0848: return false;
0849: }
0850: i1 = end1;
0851: i2 = end2;
0852: } else if (isXML
0853: && (c1 == ' ' || c1 == '\n' || c1 == '\t' || c1 == '\r')) {
0854: i1++;
0855: } else if (isXML
0856: && (c2 == ' ' || c2 == '\n' || c2 == '\t' || c2 == '\r')) {
0857: i2++;
0858: }
0859: // If isXML, then "/>" matches "></ANYNAME>".
0860: else if (isXML && c1 == '/' && c2 == '>' && i1 + 1 < len1
0861: && i2 + 2 < len2 && arg1.charAt(i1 + 1) == '>'
0862: && arg2.charAt(i2 + 1) == '<'
0863: && arg2.charAt(i2 + 2) == '/') {
0864: for (i2 = i2 + 3;; i2++) {
0865: if (i2 >= len2)
0866: return false;
0867: char c = arg2.charAt(i2);
0868: if (c == '>')
0869: break;
0870: if (!XName.isNamePart(c))
0871: return false;
0872: }
0873: i1 = i1 + 2;
0874: i2 = i2 + 1;
0875: } else
0876: return false;
0877: }
0878: }
0879:
0880: static boolean isFloatChar(int c) {
0881: return (c >= '0' && c <= '9') || c == '.' || c == '-'
0882: || c == '+' || c == 'E';
0883: }
0884:
0885: String selectedTest;
0886:
0887: public String getElementValue() {
0888: return cout.toSubString(elementStartIndex[nesting]);
0889: }
0890:
0891: public void endElement() {
0892: if (inStartTag)
0893: handleStartTag();
0894: if (tagMatches("test-case")) {
0895: if (--maxTests == 0)
0896: System.exit(0); // FIXME
0897: if (selectedTest == null || selectedTest.equals(testName)) {
0898: xqlog.startElement(testCaseElementType);
0899: writeAttribute("name", testName);
0900: try {
0901: // Other attributes and <test-case> body written by evalTest.
0902: evalTest(testName);
0903: } catch (Throwable ex) {
0904: System.err.println("test-case name:" + testName);
0905: System.err.println("caught " + ex);
0906: ex.printStackTrace();
0907: }
0908: xqlog.endElement();
0909: }
0910: //xqlog.flush();
0911: testName = null;
0912: contextItem = null;
0913: Environment env = Environment.getCurrent();
0914: while (!externalVariablesSet.empty())
0915: env.remove((Symbol) externalVariablesSet.pop());
0916: } else if (tagMatches("expected-error")) {
0917: expectedErrorsBuf.append(getElementValue());
0918: expectedErrorsBuf.append('|');
0919: } else if (tagMatches("input-query")) {
0920: String variable = attributes.getValue("variable");
0921: Symbol symbol = Symbol.parse(variable);
0922: String name = attributes.getValue("name");
0923: String filename = directory + '/' + XQueryQueryOffsetPath
0924: + testFilePath + name + XQueryFileExtension;
0925: InPort in;
0926: try {
0927: in = InPort.openFile(filename);
0928: Object value = XQuery.getInstance().eval(in);
0929: in.close();
0930: Environment current = Environment.getCurrent();
0931: current.put(symbol, null, value);
0932: externalVariablesSet.push(symbol);
0933: } catch (Throwable ex) {
0934: System.err.println("input-query: cannot open "
0935: + filename);
0936: System.err.println("caught " + ex);
0937: ex.printStackTrace();
0938: System.exit(-1);
0939: }
0940: } else if (tagMatches("input-file")
0941: || tagMatches("contextItem")) {
0942: String inputFile = getElementValue();
0943: // KLUDGE around testsuite bug!
0944: if ("userdefined".equals(inputFile))
0945: inputFile = "emptydoc";
0946: String path = directory + '/' + sources.get(inputFile);
0947:
0948: String variable;
0949: Symbol symbol;
0950: if (tagMatches("input-file")) {
0951: variable = attributes.getValue("variable");
0952: symbol = Symbol.parse(variable);
0953: externalVariablesSet.push(symbol);
0954: } else // tagMatches("contextItem")
0955: {
0956: variable = null;
0957: symbol = null;
0958: }
0959: try {
0960: Object value = gnu.kawa.xml.Document.parseCached(path);
0961: if (symbol != null)
0962: Environment.getCurrent().put(symbol, null, value);
0963: else
0964: contextItem = value;
0965: } catch (Throwable ex) {
0966: System.err.println("caught " + ex);
0967: System.err.println("reading data file " + path);
0968: System.err.println("inputFile:" + inputFile
0969: + " variable:" + variable + " path:" + path);
0970: ex.printStackTrace();
0971: System.exit(-1);
0972: }
0973: } else if (tagMatches("input-URI")) {
0974: String inputFile = getElementValue();
0975: String variable = attributes.getValue("variable");
0976: Object inputValue = sources.get(inputFile);
0977: String path = inputValue instanceof Values ? "collection:"
0978: + inputFile : "file://" + directory + '/'
0979: + inputValue;
0980: Symbol symbol = Symbol.parse(variable);
0981: externalVariablesSet.push(symbol);
0982: Environment.getCurrent().put(symbol, null,
0983: Path.valueOf(path));
0984: } else if (tagMatches("defaultCollection")) {
0985: String inputFile = getElementValue();
0986: Object inputValue = sources.get(inputFile);
0987: Object val = NodeUtils.getSavedCollection("collection:"
0988: + inputFile);
0989: NodeUtils.setSavedCollection("#default", val);
0990: } else if (tagMatches("collection")) {
0991: NodeUtils.setSavedCollection("collection:" + collectionID,
0992: collectionDocuments.canonicalize());
0993: collectionID = null;
0994: collectionDocuments = null;
0995: } else if (tagMatches("input-document")) {
0996: String inputName = getElementValue();
0997: String path = "file://" + directory + '/'
0998: + sources.get(inputName);
0999: if (collectionID == null)
1000: throw new Error("<input-document> not in <collection>");
1001: try {
1002: KDocument value = (KDocument) Document
1003: .parseCached(path);
1004: collectionDocuments.writeObject(value);
1005: } catch (Throwable ex) {
1006: System.err.println("caught " + ex);
1007: System.err.println("reading data file " + path);
1008: System.err.println("for collection " + collectionID);
1009: ex.printStackTrace();
1010: System.exit(-1);
1011: }
1012: } else if (tagMatches("output-file")) {
1013: outputFileAlts.push(getElementValue());
1014: outputCompareAlts.push(attributes.getValue("compare"));
1015: } else if (tagMatches("test-suite")) {
1016: xqlog.endElement();
1017: } else if (testName != null && tagMatches("module")) {
1018: String uri = attributes.getValue("namespace");
1019: String module = getElementValue();
1020: String mfile = (String) modules.get(module);
1021: String mpath = directory + '/' + mfile
1022: + XQueryFileExtension;
1023: String mclass = Compilation.mangleURI(uri) + '.'
1024: + XQuery.makeClassName(mpath);
1025:
1026: ModuleInfo minfo = manager.findWithClassName(mclass);
1027: minfo.sourcePath = mfile + XQueryFileExtension;
1028: minfo.setSourceAbsPath(Path.valueOf(mpath));
1029: minfo.setNamespaceUri(uri);
1030: }
1031: /*
1032: else if ("test".equals(typeName)
1033: && (nesting == 0 || (inTestSuite && nesting == 1)))
1034: {
1035: inTest = false;
1036: TestMisc.evalTest(query, expect);
1037: }
1038: else if (inTestSuite ? nesting == 2 : nesting == 1)
1039: {
1040: if ("query".equals(typeName))
1041: query = sout.toString();
1042: else if ("expect".equals(typeName))
1043: expect = sout.toString();
1044: currentTag = null;
1045: }
1046: else
1047: base.endElement();
1048: */
1049: cout.setLength(elementStartIndex[nesting]);
1050: nesting--;
1051: Object type = elementTypeStack.pop();
1052: currentElementType = type;
1053: currentElementSymbol = type instanceof Symbol ? (Symbol) type
1054: : null;
1055: }
1056:
1057: public void startAttribute(Object attrType) {
1058: super .startAttribute(attrType);
1059: attrValueStart = cout.length();
1060: }
1061:
1062: public void endAttribute() {
1063: super .endAttribute();
1064: String attrValue = cout.toSubString(attrValueStart, cout
1065: .length() - 1);
1066: Symbol sym = (Symbol) attributeType;
1067: String uri = sym.getNamespaceURI();
1068: String local = sym.getLocalPart();
1069: String prefix = sym.getPrefix();
1070: String qname = (prefix == null || prefix.length() == 0 ? local
1071: : prefix + ":" + local);
1072: cout.setLength(attrValueStart);
1073: attributes.addAttribute(uri, local, qname, "CDATA", attrValue);
1074: }
1075:
1076: public void beforeContent() {
1077: if (!inAttribute && inStartTag)
1078: handleStartTag();
1079: }
1080: }
|