package lux.compiler;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import javax.xml.transform.stream.StreamSource;
import lux.Compiler;
import lux.Compiler.SearchStrategy;
import lux.Evaluator;
import lux.QueryContext;
import lux.XdmResultSet;
import lux.exception.LuxException;
import lux.index.IndexConfiguration;
import lux.index.XmlIndexer;
import lux.xml.QName;
import lux.xpath.AbstractExpression;
import net.sf.saxon.lib.CollectionURIResolver;
import net.sf.saxon.lib.StandardCollectionURIResolver;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmAtomicValue;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmSequenceIterator;
import net.sf.saxon.s9api.XdmValue;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
/**
* Tests that focus on the compiler and optimizer, and do not rely on any stored
* documents
*/
public class CompilerTest {
protected Compiler compiler;
protected Evaluator eval;
protected SaxonTranslator translator;
@Before public void setup () {
XmlIndexer indexer = new XmlIndexer(IndexConfiguration.INDEX_QNAMES);
compiler = new Compiler(indexer.getConfiguration());
eval = new Evaluator(compiler, null, null);
translator = compiler.makeTranslator();
}
@Test
public void testSearchStrategy () throws Exception {
assertSame (SearchStrategy.LUX_SEARCH, compiler.getSearchStrategy());
compiler.setSearchStrategy(SearchStrategy.NONE);
assertSame (SearchStrategy.NONE, compiler.getSearchStrategy());
}
@Test
public void testResolver () throws Exception {
CollectionURIResolver resolver = compiler.getDefaultCollectionURIResolver();
assertNotNull (resolver);
assertTrue (resolver.getClass().getName(), resolver instanceof StandardCollectionURIResolver);
}
@Test
public void testInititalizeEXPath () throws Exception {
System.setProperty("org.expath.pkg.saxon.repo", "fail");
// ensure that Compiler can be created with invalid EXPath repo - just logs an error
new Compiler(new IndexConfiguration());
System.setProperty("org.expath.pkg.saxon.repo", "");
}
@Test public void testXPathCompiler() throws SaxonApiException {
XPathExecutable ex = compiler.getXPathCompiler().compile("/");
AbstractExpression expr =
new SaxonTranslator(compiler.getProcessor().getUnderlyingConfiguration()).
exprFor(ex.getUnderlyingExpression().getInternalExpression());
assertEquals ("(/)", expr.toString());
}
@Test
public void testModuleImport () throws Exception {
assertQuery ("test", "import-module.xqy");
}
@Test
public void testTypedVariable() throws Exception {
assertQuery ("13", "typed-variable.xqy");
}
@Test
public void testTypedFunction() throws Exception {
assertQueryError ("A sequence of more than one item is not allowed as the result of function local:int-sequence() (1, 2) ", "typed-function.xqy");
}
@Test
public void testTypedNodes() throws Exception {
assertQuery ("2", "typed-nodes.xqy");
}
@Test
public void testTypedNodeFail() throws Exception {
assertQueryError ("Required item type of result of function local:maybe-foo() is element(Q{}foo); supplied value has item type element(Q{}bar)", "typed-node-fail.xqy");
}
@Test
public void testExtVarType() throws Exception {
XQueryExecutable query = compileQuery ("ext-var-type.xqy");
QueryContext context = new QueryContext ();
context.bindVariable(new QName("http://localhost/", "integer"), new XdmAtomicValue("one"));
// query expects an integer, not a string
XdmResultSet result = eval.evaluate(query, context);
assertEquals ("expected one error", 1, result.getErrors().size());
assertEquals ("Required item type of value of variable $local:integer is xs:integer; supplied value has item type xs:string", result.getErrors().get(0).getMessage());
}
@Test
public void testExtVar () throws Exception {
XQueryExecutable query = compileQuery ("ext-var.xqy");
QueryContext context = new QueryContext ();
context.bindVariable(new QName("http://localhost/", "integer"), new XdmAtomicValue("1"));
XdmResultSet result = eval.evaluate(query, context);
assertEquals ("expected no results", false, result.getXdmValue().iterator().hasNext());
}
@Test
public void testCommentConstructor() throws Exception {
assertQuery ("<!--test-->", "comment-constructor.xqy");
}
@Test
public void testPIConstructor() throws Exception {
assertQuery ("<?php test?>", "processing-instruction-constructor.xqy");
}
@Test
public void testElementConstructor() throws Exception {
assertQuery ("<xml:Father xml:id=\"Bosak\">John Bosak</xml:Father>", "element-constructor.xqy");
}
@Test
public void testInstanceOf() throws Exception {
assertQuery ("test,testtest1,test", "instance-of.xqy");
}
@Test
public void testSatisfies() throws Exception {
assertQuery ("yesno", "satisfies.xqy");
}
@Test
public void testIntersect() throws Exception {
assertQuery ("test", "intersect.xqy");
}
@Test
public void testMinus() throws Exception {
assertQuery ("1", "minus.xqy");
assertQuery ("1", "minus-1.xqy");
}
@Test
public void testVariableShadowing () throws Exception {
assertQuery ("1", "variable-shadowing.xqy");
}
@Test
public void testWhereClause () throws Exception {
// this is supposed to test the translation and optimization of where clauses (a bit)
// but Saxon converts where clauses to predicates pretty much universally, so
// this uses an "at" expression to force the where clause to be retained
assertQuery ("4", "count-primes-less-than-10.xqy");
}
@Test
public void testParentExpression () throws Exception {
assertQuery ("2", "count-parents.xqy");
}
@Test
public void testComputedAttributeName () throws Exception {
assertQueryError (null, "computed-attribute-name.xqy");
}
@Test
public void testK2ForExprWithout10 () throws Exception {
// exercise some obscure Saxon code rewriting path involving LetClause
assertQuery ("111222333", "K2-ForExprWithout-10.xqy");
}
@Test
public void testFollowing () throws Exception {
assertQuery ("true", "following.xqy");
}
@Test
public void testEmptyVariable() throws Exception {
assertQuery ("", "empty-variable.xqy");
}
@Test
public void testOptionalVariable() throws Exception {
assertQuery ("b", "optional-variable.xqy");
}
@Test
public void testNodeNameWildcards () throws Exception {
assertQuery ("a xs:bxs:ba", "node-wildcard.xqy");
}
@Test
public void testStrayOperators () throws Exception {
assertQuery ("-2", "stray-operators.xqy");
}
@Test
public void testForAt() throws Exception {
assertQuery ("1148", "for-at.xqy");
}
@Test
public void testReverse() throws Exception {
assertQuery ("10", "reverse.xqy");
}
@Test
public void testIntegerRange() throws Exception {
assertQuery ("true", "integer-range.xqy");
}
@Test
public void testGeneralizeOperator() throws Exception {
QueryContext context = new QueryContext();
String input = "<works><employee name='Jane Doe 1' gender='female'><empnum>E1</empnum><pnum>P1</pnum><hours>40</hours></employee></works>";
XdmNode inputDoc = compiler.getProcessor().newDocumentBuilder().build(new StreamSource (new StringReader (input)));
context.bindVariable(new QName("input-context1"), inputDoc);
assertQuery ("true", "generalize-operator.xqy", context);
}
@Test
public void testReturnExpr018() throws Exception {
QueryContext context = new QueryContext();
String input = "<Drive id='0'><Folder><File><Stream><StreamSize>10</StreamSize></Stream></File></Folder></Drive>";
XdmNode inputDoc = compiler.getProcessor().newDocumentBuilder().build(new StreamSource (new StringReader (input)));
context.bindVariable(new QName("input-context"), inputDoc);
assertQuery ("falsetruefalsetruefalsetrue", "ReturnExpr018.xqy", context);
}
@Test
public void testDateSubtraction() throws Exception {
//assertQuery ("xs:dayTimeDuration(\"P26997D\")", "day-time.xqy");
assertQuery ("P26997D", "day-time.xqy");
}
private void assertQuery (String result, String queryFileName) throws IOException, LuxException, URISyntaxException {
assertQuery (result, queryFileName, null);
}
private void assertQuery (String result, String queryFileName, QueryContext context) throws IOException, LuxException, URISyntaxException {
XdmResultSet resultSet = evalQuery(queryFileName, context);
if (resultSet.getErrors().size() > 0) {
fail ("Got unexpected error: " + resultSet.getErrors().get(0).getMessageAndLocation());
}
XdmValue value = resultSet.getXdmValue();
StringBuilder buf = new StringBuilder();
XdmSequenceIterator iter = value.iterator();
while (iter.hasNext()) {
buf.append(iter.next());
}
assertEquals (result, buf.toString());
}
private XdmResultSet evalQuery(String queryFileName, QueryContext context) throws IOException, URISyntaxException {
XQueryExecutable cq = compileQuery(queryFileName);
return eval.evaluate(cq, context);
}
private XQueryExecutable compileQuery(String queryFileName) throws IOException, URISyntaxException {
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource ("lux/compiler/" + queryFileName);
String query = IOUtils.toString(url.openStream(), "utf-8");
URI uri = url.toURI();
XQueryExecutable cq = compiler.compile(query, null, uri, null);
// System.err.println (compiler.getLastOptimized());
return cq;
}
private void assertQueryError (String error, String queryFileName) throws IOException, URISyntaxException {
assertQueryError (error, queryFileName, null);
}
private void assertQueryError (String error, String queryFileName, QueryContext context) throws IOException, URISyntaxException {
XdmResultSet result = evalQuery (queryFileName, context);
assertFalse ("expected exception '" + error + "' not thrown; got results=" + result.getXdmValue(),
result.getErrors().isEmpty());
if (error != null) {
assertEquals (error, result.getErrors().get(0).getMessage());
}
}
}