package org.basex.tests.w3c; import static org.basex.tests.w3c.QT3Constants.*; import static org.basex.util.Prop.*; import static org.basex.util.Token.*; import java.io.*; import java.lang.reflect.*; import java.util.*; import java.util.Map.Entry; import java.util.regex.*; import javax.xml.namespace.*; import org.basex.core.*; import org.basex.io.*; import org.basex.io.out.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.func.*; import org.basex.query.func.fn.DeepEqual.Mode; import org.basex.query.value.item.*; import org.basex.query.value.type.*; import org.basex.tests.bxapi.*; import org.basex.tests.bxapi.xdm.*; import org.basex.util.*; import org.basex.util.options.Options.YesNo; /** * Driver for the XQuery/XPath/XSLT 3.* Test Suite, located at * {@code http://dev.w3.org/2011/QT3-test-suite/}. The driver needs to be * executed from the test suite directory. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ public final class QT3TS extends Main { /** EQName pattern. */ private static final Pattern BIND = Pattern.compile("^Q\\{(.*?)\\}(.+)$"); /** Test suite id. */ private static final String TESTID = "qt3ts"; /** Path to the test suite (ignored if {@code null}). */ private String basePath; /** Maximum length of result output. */ private int maxout = 2000; /** Correct results. */ private final TokenBuilder right = new TokenBuilder(); /** Wrong results. */ private final TokenBuilder wrong = new TokenBuilder(); /** Ignored tests. */ private final TokenBuilder ignore = new TokenBuilder(); /** Report builder. */ private QT3TSReport report; /** Number of total queries. */ private int total; /** Number of tested queries. */ private int tested; /** Number of correct queries. */ private int correct; /** Number of ignored queries. */ private int ignored; /** Current base uri. */ private String baseURI; /** Current base directory. */ private String baseDir; /** Slow queries flag. */ private TreeMap<Long, String> slow; /** Query filter string. */ private String single = ""; /** Verbose flag. */ private boolean verbose; /** Error code flag. */ private boolean errors = true; /** Also print ignored files. */ private boolean ignoring; /** All flag. */ private boolean all; /** Database context. */ final Context ctx = new Context(); /** Global environments. */ private final ArrayList<QT3Env> genvs = new ArrayList<>(); /** * Main method of the test class. * @param args command-line arguments * @throws Exception exception */ public static void main(final String... args) throws Exception { try { new QT3TS(args).run(); } catch(final IOException ex) { Util.errln(ex); System.exit(1); } } /** * Constructor. * @param args command-line arguments */ QT3TS(final String[] args) { super(args); } /** * Runs all tests. * @throws Exception exception */ private void run() throws Exception { ctx.soptions.set(StaticOptions.DBPATH, sandbox().path() + "/data"); parseArgs(); init(); final Performance perf = new Performance(); ctx.options.set(MainOptions.CHOP, false); final SerializerOptions sopts = new SerializerOptions(); sopts.set(SerializerOptions.METHOD, SerialMethod.XML); sopts.set(SerializerOptions.INDENT, YesNo.NO); sopts.set(SerializerOptions.OMIT_XML_DECLARATION, YesNo.NO); ctx.options.set(MainOptions.SERIALIZER, sopts); final XdmValue doc = new XQuery("doc('" + file(false, CATALOG) + "')", ctx).value(); final String version = asString("*:catalog/@version", doc); Util.outln(NL + "QT3 Test Suite " + version); Util.outln("Test directory: " + file(false, ".")); Util.out("Parsing queries"); for(final XdmItem ienv : new XQuery("*:catalog/*:environment", ctx).context(doc)) genvs.add(new QT3Env(ctx, ienv)); for(final XdmItem it : new XQuery("for $f in //*:test-set/@file return string($f)", ctx).context(doc)) testSet(it.getString()); final StringBuilder result = new StringBuilder(); result.append(" Rate : ").append(pc(correct, tested)).append(NL); result.append(" Total : ").append(total).append(NL); result.append(" Tested : ").append(tested).append(NL); result.append(" Wrong : ").append(tested - correct).append(NL); result.append(" Ignored : ").append(ignored).append(NL); // save log data Util.outln(NL + "Writing log file '" + TESTID + ".log'..."); try(PrintOutput po = new PrintOutput(TESTID + ".log")) { po.println("QT3TS RESULTS __________________________" + NL); po.println(result.toString()); po.println("WRONG __________________________________" + NL); po.print(wrong.finish()); if(all || !single.isEmpty()) { po.println("CORRECT ________________________________" + NL); po.print(right.finish()); } if(ignoring) { po.println("IGNORED ________________________________" + NL); po.print(ignore.finish()); } } // save report if(report != null) { sopts.set(SerializerOptions.OMIT_XML_DECLARATION, YesNo.YES); final String file = "ReportingResults/results_" + NAME + '_' + VERSION + IO.XMLSUFFIX; new IOFile(file).write(report.create(ctx)); Util.outln("Creating report '" + file + "'..."); } Util.out(NL + result); Util.outln(" Time : " + perf); if(slow != null && !slow.isEmpty()) { Util.outln(NL + "Slow queries:"); for(final Entry<Long, String> l : slow.entrySet()) { Util.outln("- " + -(l.getKey() / 1000000) + " ms: " + l.getValue()); } } ctx.close(); sandbox().delete(); } /** * Runs a single test set. * @param name name of test set * @throws Exception exception */ private void testSet(final String name) throws Exception { final XdmValue doc = new XQuery("doc(' " + file(false, name) + "')", ctx).value(); final XdmValue set = new XQuery("*:test-set", ctx).context(doc).value(); final IO base = IO.get(doc.getBaseURI()); baseURI = base.path(); baseDir = base.dir(); if(supported(set)) { // parse environment of test-set final ArrayList<QT3Env> envs = new ArrayList<>(); for(final XdmItem ienv : new XQuery("*:environment", ctx).context(set)) envs.add(new QT3Env(ctx, ienv)); if(report != null) report.addSet(asString("@name", set)); // run all test cases for(final XdmItem its : new XQuery("*:test-case", ctx).context(set)) { try { testCase(its, envs); } catch(final IOException ex) { Util.debug(ex); } } } } /** * Runs a single test case. * @param test node * @param envs environments * @throws Exception exception */ private void testCase(final XdmItem test, final ArrayList<QT3Env> envs) throws Exception { if(total++ % 500 == 0) Util.out("."); if(!supported(test)) { if(ignoring) ignore.add(asString("@name", test)).add(NL); ignored++; return; } // skip queries that do not match filter final String name = asString("@name", test); if(!name.startsWith(single)) { if(ignoring) ignore.add(name).add(NL); ignored++; return; } tested++; // expected result final XdmValue expected = new XQuery("*:result/*[1]", ctx).context(test).value(); // check if environment is defined in test-case QT3Env env = null; final XdmValue ienv = new XQuery("*:environment[*]", ctx).context(test).value(); if(ienv.size() != 0) env = new QT3Env(ctx, ienv); // parse local environment boolean base = true; if(env == null) { final String e = asString("*:environment[1]/@ref", test); if(!e.isEmpty()) { // check if environment is defined in test-set env = envs(envs, e); // check if environment is defined in catalog if(env == null) { env = envs(genvs, e); base = false; } if(env == null) Util.errln("%: environment '%' not found.", name, e); } } // retrieve query to be run final Performance perf = new Performance(); final String qfile = asString("*:test/@file", test); String string; if(qfile.isEmpty()) { // get query string string = asString("*:test", test); } else { // get query from file string = string(new IOFile(baseDir, qfile).read()); } if(verbose) Util.outln(name); // bind variables if(env != null) { for(final HashMap<String, String> par : env.params) { final String decl = par.get(DECLARED); if(decl == null || decl.equals("false")) { string = "declare variable $" + par.get(NNAME) + " external;" + string; } } // bind documents for(final HashMap<String, String> src : env.sources) { final String role = src.get(ROLE); if(role != null && role.startsWith("$")) { string = "declare variable " + role + " external;" + string; } } } final XQuery query = new XQuery(string, ctx); if(base) query.baseURI(baseURI); // add modules final String qm = "for $m in *:module return ($m/@uri, $m/@file)"; final XQuery qmod = new XQuery(qm, ctx).context(test); while(true) { final XdmItem uri = qmod.next(); if(uri == null) break; final XdmItem file = qmod.next(); if(file == null) break; query.addModule(uri.getString(), new IOFile(baseDir, file.getString()).path()); } final QT3Result returned = new QT3Result(); returned.env = env; try { environment(query, env); if(env != null) { // bind documents for(final HashMap<String, String> src : env.sources) { // add document reference final String file = file(base, src.get(FILE)); query.addDocument(src.get(URI), file); final String role = src.get(ROLE); if(role == null) continue; final XdmValue doc = query.document(file); if(role.equals(".")) query.context(doc); else query.bind(role, doc); } // bind resources for(final HashMap<String, String> src : env.resources) { query.addResource(src.get(URI), file(base, src.get(FILE)), src.get(QT3Constants.ENCODING)); } // bind collections query.addCollection(env.collURI, env.collSources.toArray()); if(env.collContext) { query.context(query.collection(env.collURI)); } // bind context item if(env.context != null) { query.context(env.context); } // set base uri if(env.baseURI != null) query.baseURI(env.baseURI); // bind decimal formats for(final Entry<QName, HashMap<String, String>> df : env.decFormats.entrySet()) { query.decimalFormat(df.getKey(), df.getValue()); } } // run query returned.value = query.value(); returned.query = query; } catch(final XQueryException ex) { returned.xqerror = ex; returned.value = null; } catch(final Throwable ex) { // unexpected error (potential bug) returned.error = ex; Util.errln("Query: " + name); Util.stack(ex); } if(slow != null) { final long l = perf.time(); if(l > 100000000) slow.put(-l, name); } final String exp = test(returned, expected); final TokenBuilder tmp = new TokenBuilder(); tmp.add(name).add(NL); tmp.add(QueryProcessor.removeComments(string, maxout)).add(NL); boolean err = returned.value == null; String res; try { if(returned.error != null) { res = returned.error.toString(); } else if(returned.xqerror != null) { res = returned.xqerror.getCode() + ": " + returned.xqerror.getLocalizedMessage(); } else { res = serialize(returned); } } catch(final XQueryException ex) { res = ex.getCode() + ": " + ex.getLocalizedMessage(); err = true; } catch(final Throwable ex) { res = returned.value.toString(); } tmp.add(err ? "Error : " : "Result: ").add(normSpecial(res)).add(NL); if(exp == null) { tmp.add(NL); right.add(tmp.finish()); correct++; } else { wrong.add(tmp.add("Expect: ").add(normSpecial(exp)).add(NL).add(NL).finish()); } if(report != null) report.addTest(name, exp == null); } /** * Assigns the query environment. * @param query query * @param env environment * @return query; */ private XQuery environment(final XQuery query, final QT3Env env) { if(env != null) { // bind namespaces for(final HashMap<String, String> ns : env.namespaces) { query.namespace(ns.get(PREFIX), ns.get(URI)); } // bind variables for(final HashMap<String, String> par : env.params) { query.bind(par.get(NNAME), new XQuery(par.get(SELECT), ctx).value()); } } return query; } /** * Normalizes special characters in the specified string. * @param in input string * @return result */ private String normSpecial(final String in) { return in.replaceAll("^<\\?xml.*?>\r?\n?", "").replace("\n", "%0A").replace("\r", "%0D"). replace("\t", "%09"); } /** Flags for dependencies that are not supported. */ private static final String NOSUPPORT = "('schema-location-hint','schemaImport','schemaValidation','staticTyping','typedData')"; /** * Checks if the current test case is supported. * @param test test case * @return result of check */ private boolean supported(final XdmValue test) { // the following query generates a result if the specified test is not supported return new XQuery( "*:environment/*:collation |" + // skip collation tests "*:dependency[" + // skip schema imports, schema validation, namespace axis, static typing "@type = 'feature' and (" + " @value = " + NOSUPPORT + " and (@satisfied = 'true' or empty(@satisfied)) or" + " @value != " + NOSUPPORT + "and @satisfied = 'false') or " + // skip fully-normalized unicode tests "@type = 'unicode-normalization-form' and @value = 'FULLY-NORMALIZED' or " + // skip xml/xsd 1.1 tests "@type = ('xml-version', 'xsd-version') and @value = ('1.1', '1.0:4-') or " + // skip default-language "@type = 'default-language' and @value != 'en' or " + // skip non-XQuery tests "@type = 'spec' and not(matches(@value, 'XQ(\\d\\d\\+|31)'))" + ']', ctx).context(test).value().size() == 0; } /** * Returns the specified environment, or {@code null}. * @param envs environments * @param ref reference * @return environment */ private static QT3Env envs(final ArrayList<QT3Env> envs, final String ref) { for(final QT3Env e : envs) if(e.name.equals(ref)) return e; return null; } /** * Tests the result of a test case. * @param result query result * @param expected expected result * @return {@code null} if test was successful; otherwise, expected test suite result */ private String test(final QT3Result result, final XdmValue expected) { final String type = expected.getName().getLocalPart(); try { final String msg; if(type.equals("error")) { msg = assertError(result, expected); } else if(type.equals("assert-serialization-error")) { msg = assertSerializationError(result, expected); } else if(type.equals("all-of")) { msg = allOf(result, expected); } else if(type.equals("not")) { msg = not(result, expected); } else if(type.equals("any-of")) { msg = anyOf(result, expected); } else if(result.value != null) { switch(type) { case "assert": msg = assertQuery(result, expected); break; case "assert-count": msg = assertCount(result, expected); break; case "assert-deep-eq": msg = assertDeepEq(result, expected); break; case "assert-empty": msg = assertEmpty(result); break; case "assert-eq": msg = assertEq(result, expected); break; case "assert-false": msg = assertBoolean(result, false); break; case "assert-permutation": msg = assertPermutation(result, expected); break; case "assert-xml": msg = assertXML(result, expected); break; case "serialization-matches": msg = serializationMatches(result, expected); break; case "assert-string-value": msg = assertStringValue(result, expected); break; case "assert-true": msg = assertBoolean(result, true); break; case "assert-type": msg = assertType(result, expected); break; default: msg = "Test type not supported: " + type; break; } } else { msg = expected.toString(); } return msg; } catch(final Exception ex) { Util.stack(ex); return "Exception: " + ex.getMessage(); } } /** * Tests error. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertError(final QT3Result result, final XdmValue expected) { final String exp = asString('@' + CODE, expected); if(result.xqerror == null) return exp; if(!errors || exp.equals("*")) return null; final QNm resErr = result.xqerror.getException().qname(); String name = exp, uri = string(QueryText.ERROR_URI); final Matcher m = BIND.matcher(exp); if(m.find()) { uri = m.group(1); name = m.group(2); } final QNm expErr = new QNm(name, uri); return expErr.eq(resErr) ? null : exp; } /** * Tests not. * @param result query result * @param exp expected result * @return optional expected test suite result */ private String not(final QT3Result result, final XdmValue exp) { final TokenBuilder tb = new TokenBuilder(); for(final XdmItem it : environment(new XQuery("*", ctx), result.env).context(exp)) { final String msg = test(result, it); if(msg != null) tb.add(tb.isEmpty() ? "" : ", ").add(msg); } return tb.isEmpty() ? "not(...)" : null; } /** * Tests all-of. * @param result query result * @param exp expected result * @return optional expected test suite result */ private String allOf(final QT3Result result, final XdmValue exp) { final TokenBuilder tb = new TokenBuilder(); for(final XdmItem it : environment(new XQuery("*", ctx), result.env).context(exp)) { final String msg = test(result, it); if(msg != null) tb.add(tb.isEmpty() ? "" : ", ").add(msg); } return tb.isEmpty() ? null : tb.toString(); } /** * Tests any-of. * @param result query result * @param exp expected result * @return optional expected test suite result */ private String anyOf(final QT3Result result, final XdmValue exp) { final TokenBuilder tb = new TokenBuilder(); for(final XdmItem it : environment(new XQuery("*", ctx), result.env).context(exp)) { final String msg = test(result, it); if(msg == null) return null; tb.add(tb.isEmpty() ? "" : ", ").add(msg); } return "any of { " + tb + " }"; } /** * Tests assertion. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertQuery(final QT3Result result, final XdmValue expected) { final String exp = expected.getString(); try { final String qu = "declare variable $result external; " + exp; return environment(new XQuery(qu, ctx), result.env).bind("$result", result.value). value().getBoolean() ? null : exp; } catch(final XQueryException ex) { // should not occur return ex.getException().getMessage(); } } /** * Tests count. * @param result query result * @param expected expected result * @return optional expected test suite result */ private static String assertCount(final QT3Result result, final XdmValue expected) { final long exp = expected.getInteger(); final int res = result.value.size(); return exp == res ? null : Util.info("% items (% found)", exp, res); } /** * Tests equality. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertEq(final QT3Result result, final XdmValue expected) { final String exp = expected.getString(); try { final String qu = "declare variable $returned external; $returned eq " + exp; return environment(new XQuery(qu, ctx), result.env).bind("$returned", result.value). value().getBoolean() ? null : exp; } catch(final XQueryException err) { // numeric overflow: try simple string comparison return err.getCode().equals("FOAR0002") && exp.equals(expected.getString()) ? null : err.getException().getMessage(); } } /** * Tests deep equals. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertDeepEq(final QT3Result result, final XdmValue expected) { final XdmValue exp = environment(new XQuery(expected.getString(), ctx), result.env).value(); return exp.deepEqual(result.value) ? null : exp.toString(); } /** * Tests permutation. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertPermutation(final QT3Result result, final XdmValue expected) { // cache expected results final HashSet<String> exp = new HashSet<>(); for(final XdmItem it : environment(new XQuery(expected.getString(), ctx), result.env)) exp.add(it.getString()); // cache actual results final HashSet<String> res = new HashSet<>(); for(final XdmItem it : result.value) res.add(it.getString()); if(exp.size() != res.size()) return Util.info("% results (found: %)", exp.size(), res.size()); for(final String s : exp.toArray(new String[exp.size()])) { if(!res.contains(s)) return Util.info("% (missing)", s); } for(final String s : res.toArray(new String[exp.size()])) { if(!exp.contains(s)) return Util.info("% (missing in expected result)", s); } return null; } /** * Tests the serialized result. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertXML(final QT3Result result, final XdmValue expected) { final String file = asString("@file", expected); final boolean norm = asBoolean("@normalize-space=('true','1')", expected); final boolean pref = asBoolean("@ignore-prefixes=('true','1')", expected); String exp = expected.getString(); try { if(!file.isEmpty()) exp = string(new IOFile(baseDir, file).read()); exp = normNL(exp); if(norm) exp = string(normalize(token(exp))); final XdmValue returned = result.value; final String res = normNL( asString("serialize(., map{ 'indent':'no','method':'xml' })", returned)); if(exp.equals(res)) return null; final String r = normNL(asString( "serialize(., map{ 'indent':'no','method':'xml','omit-xml-declaration':'no' })", returned)); if(exp.equals(r)) return null; // include check for comments, processing instructions and namespaces String flags = "'" + Mode.ALLNODES + '\''; if(!pref) flags += ",'" + Mode.NAMESPACES + '\''; final String query = Function._UTIL_DEEP_EQUAL.args("<X>" + exp + "</X>", "<X>" + res + "</X>" , '(' + flags + ')'); return asBoolean(query, expected) ? null : exp; } catch(final IOException ex) { return Util.info("% (found: %)", exp, ex); } } /** * Tests the serialized result. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String serializationMatches(final QT3Result result, final XdmValue expected) { try { final String flags = asString("@flags", expected); final XdmValue ret = XdmValue.get(Str.get(serialize(result))); final String qu = "declare variable $returned external;" + "declare variable $expected external;" + "matches($returned, string($expected), '" + flags + "')"; return environment(new XQuery(qu, ctx), result.env).bind("returned", ret). bind("expected", expected).value().getBoolean() ? null : expected.getString(); } catch(final Exception err) { return Util.info("% (found: %)", expected.getString(), err); } } /** * Tests a serialization error. * @param result returned result * @param expected expected result * @return optional expected test suite result */ private String assertSerializationError(final QT3Result result, final XdmValue expected) { final String expCode = asString('@' + CODE, expected); if(result.xqerror != null) { if(!errors || expCode.equals("*")) return null; final String resCode = string(result.xqerror.getException().qname().local()); if(resCode.equals(expCode)) return null; } try { if(result.value != null) serialize(result); return expCode; } catch(final QueryException ex) { if(!errors || expCode.equals("*")) return null; final String resCode = string(ex.qname().local()); if(resCode.equals(expCode)) return null; return Util.info("% (found: %)", expCode, ex); } catch(final IOException ex) { return Util.info("% (found: %)", expCode, ex); } } /** * Serializes values. * @param result query result * @return optional expected test suite result * @throws QueryException query exception * @throws IOException I/O exception */ private static String serialize(final QT3Result result) throws QueryException, IOException { try { final ArrayOutput ao = new ArrayOutput(); try(Serializer ser = result.query.qp().getSerializer(ao)) { for(final Item it : result.value.internal()) ser.serialize(it); } return ao.toString(); } catch(final QueryIOException ex) { throw ex.getCause(); } } /** * Tests string value. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertStringValue(final QT3Result result, final XdmValue expected) { String exp = expected.getString(); // normalize space final boolean norm = asBoolean("@normalize-space=('true','1')", expected); if(norm) exp = string(normalize(token(exp))); final TokenBuilder tb = new TokenBuilder(); int c = 0; for(final XdmItem it : result.value) { if(c++ != 0) tb.add(' '); tb.add(it.getString()); } final String res = norm ? string(normalize(tb.finish())) : tb.toString(); return exp.equals(res) ? null : exp; } /** * Tests boolean. * @param result query result * @param expected expected * @return optional expected test suite result */ private static String assertBoolean(final QT3Result result, final boolean expected) { final XdmValue returned = result.value; return returned.getType().eq(SeqType.BLN) && returned.getBoolean() == expected ? null : Util.info(expected); } /** * Tests empty sequence. * @param result query result * @return optional expected test suite result */ private static String assertEmpty(final QT3Result result) { return result.value == XdmEmpty.EMPTY ? null : ""; } /** * Tests type. * @param result query result * @param expected expected result * @return optional expected test suite result */ private String assertType(final QT3Result result, final XdmValue expected) { final String exp = expected.getString(); try { final String qu = "declare variable $returned external; $returned instance of " + exp; final XQuery query = environment(new XQuery(qu, ctx), result.env); final XdmValue returned = result.value; return query.bind("returned", returned).value().getBoolean() ? null : Util.info("Type '%' (found: '%')", exp, returned.getType().toString()); } catch(final XQueryException ex) { // should not occur return ex.getException().getMessage(); } } /** * Returns the string representation of a query result. * @param query query string * @param value optional context value * @return optional expected test suite result */ String asString(final String query, final XdmValue value) { return XQuery.string(query, value, ctx); } /** * Returns the boolean representation of a query result. * @param query query string * @param value optional context value * @return optional expected test suite result */ boolean asBoolean(final String query, final XdmValue value) { final XdmValue val = new XQuery(query, ctx).context(value).value(); return val.size() != 0 && val.getBoolean(); } /** * Returns the path to a given file. * @param base base flag. * @param file file name * @return path to the file */ private String file(final boolean base, final String file) { final String dir = base ? baseDir : basePath; return dir == null ? file : new IOFile(dir, file).path(); } /** * Calculates the percentage of correct queries. * @param v value * @param t total value * @return percentage */ private static String pc(final int v, final long t) { return (t == 0 ? 100 : v * 10000 / t / 100.0d) + "%"; } /** * Normalizes newline characters. * @param in input string * @return result */ private static String normNL(final String in) { return in.replaceAll("\r\n|\r|\n", NL); } @Override protected void parseArgs() throws IOException { final MainParser arg = new MainParser(this); while(arg.more()) { if(arg.dash()) { final char c = arg.next(); if(c == 'v') { verbose = true; } else if(c == 'a') { all = true; } else if(c == 'd') { debug = true; } else if(c == 'i') { ignoring = true; } else if(c == 'e') { errors = false; } else if(c == 'r') { report = new QT3TSReport(); } else if(c == 's') { slow = new TreeMap<>(); } else if(c == 'p') { final File f = new File(arg.string()); if(!f.isDirectory()) throw arg.usage(); basePath = f.getCanonicalPath(); } else { throw arg.usage(); } } else { single = arg.string(); maxout = Integer.MAX_VALUE; } } } /** * Adds an environment variable required for function tests. Reference: *http://stackoverflow.com/questions/318239/how-do-i-set-environment-variables-from-java */ @SuppressWarnings("unchecked") private static void init() { final Map<String, String> ne = new HashMap<>(); ne.put("QTTEST", "42"); ne.put("QTTEST2", "other"); ne.put("QTTESTEMPTY", ""); try { final Class<?> pe = Class.forName("java.lang.ProcessEnvironment"); final Field f = pe.getDeclaredField("theEnvironment"); f.setAccessible(true); ((Map<String, String>) f.get(null)).putAll(ne); final Field f2 = pe.getDeclaredField("theCaseInsensitiveEnvironment"); f2.setAccessible(true); ((Map<String, String>) f2.get(null)).putAll(ne); } catch(final NoSuchFieldException ex) { try { for(final Class<?> cl : Collections.class.getDeclaredClasses()) { if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { final Field f = cl.getDeclaredField("m"); f.setAccessible(true); ((Map<String, String>) f.get(System.getenv())).putAll(ne); } } } catch(final Exception e2) { Util.errln("Test environment variable could not be set:" + e2); } } catch(final Exception e1) { Util.errln("Test environment variable could not be set: " + e1); } } /** * Structure for storing XQuery results. */ static class QT3Result { /** Test environment. */ QT3Env env; /** Query instance. */ XQuery query; /** Query result. */ XdmValue value; /** Query exception. */ XQueryException xqerror; /** Query error. */ Throwable error; } /** * Returns the sandbox database path. * @return database path */ private IOFile sandbox() { return new IOFile(TMP, TESTID); } @Override public String header() { return Util.info(Text.S_CONSOLE_X, Util.className(this)); } @Override public String usage() { return " -v [pat]" + NL + " [pat] perform tests starting with a pattern" + NL + " -a save all tests" + NL + " -d debugging mode" + NL + " -e ignore error codes" + NL + " -i also save ignored files" + NL + " -p path to the test suite" + NL + " -r generate report file" + NL + " -s print slow queries" + NL + " -v verbose output"; } }