package lux.saxon; import static org.junit.Assert.assertEquals; import lux.Compiler; import lux.Evaluator; import lux.compiler.PathOptimizer; import lux.compiler.SaxonTranslator; import lux.exception.LuxException; import lux.xquery.XQuery; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XQueryExecutable; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; // Note: A number of these tests now fail since we now have added some alternate serialization for // elements. The test was useful during development, but too brittle to really maintain long term. // We should consider abandoning this now that we have the XQTS test suite working since it really // covers the same ground in a much more robust way. public class TranslatorTest { private Evaluator eval; @Before public void setup () { eval = new Evaluator(); } @Test @Ignore public void testTranslate () { roundtrip ("/"); roundtrip ("."); roundtrip ("foo"); roundtrip ("//foo/bar"); roundtrip ("//foo[ancestor::bar]"); roundtrip ("concat('a','b')"); roundtrip ("descendant::foo[1]"); roundtrip ("descendant::foo[2]"); roundtrip ("descendant::foo[last()]"); roundtrip ("false()"); // saxon applies an interesting optimization to this one: // count(//bar) becomes count(subsequence(//bar,1,2)) roundtrip ("(1 eq count(//bar)) and ../foo"); roundtrip ("count(//bar) eq 10"); } @Test @Ignore public void testSetOperations() { roundtrip ("node() except comment()"); roundtrip ("node() union comment()"); roundtrip ("//a//b intersect //c//b"); roundtrip ("a except b/a"); roundtrip ("(a | b | c) except b"); } @Test @Ignore public void testTypeCasts () { roundtrip ("//a[xs:integer(@x) gt 2] intersect //a[@y='dog']"); } @Test @Ignore public void testBooleanOperations() { roundtrip ("node() or ../foo"); roundtrip ("node() and self::foo"); roundtrip ("true() or false()"); roundtrip ("true() and false()"); roundtrip ("not(node() or ../foo)"); roundtrip ("not(node() and self::foo)"); roundtrip ("not(node()) or ../foo"); roundtrip ("not(node()) and self::foo"); roundtrip ("true() or false()"); roundtrip ("true() and false()"); } // test fails due to funkiness with AtomicSequenceConverter the queries // seem equivalent, but we end up with data() in one of them? @Test @Ignore public void testMathOperations () { roundtrip ("x + y"); roundtrip ("x - y"); roundtrip ("x * y"); roundtrip ("x div y"); roundtrip ("x idiv y"); roundtrip ("x mod y"); roundtrip ("count(x) + count(y)"); roundtrip ("-x"); roundtrip ("+x"); } @Test public void testAtomicComparisons () { roundtrip ("x eq y"); roundtrip ("x ne y"); roundtrip ("x gt y"); roundtrip ("x lt y"); roundtrip ("x ge y"); roundtrip ("x le y"); } @Test public void testGeneralComparisons () { roundtrip ("x = y"); roundtrip ("x != y"); roundtrip ("x > y"); roundtrip ("x < y"); roundtrip ("x >= y"); roundtrip ("x <= y"); } @Test public void testIdentityComparisons () { roundtrip ("x is y"); roundtrip ("x << y"); roundtrip ("x >> y"); } @Test public void testDocumentNodeTest () { roundtrip ("self::document-node()"); roundtrip ("foo | self::document-node(element(bar))"); } @Test public void testNodeTest () { roundtrip ("a|b"); roundtrip (".//*[self::a or self::b]"); roundtrip ("text()"); roundtrip (".//text()"); roundtrip ("processing-instruction()"); roundtrip ("processing-instruction('process')"); } @Test @Ignore public void testLetExpr () { // Saxon takes this one out into xquery land, representing as a let $x := () return xxx($x)?? roundtrip ("(a,b,c)[//foo/@count + 1]"); roundtrip ("preceding::*[count(//*)]"); roundtrip ("(preceding::*)[count(//*)]"); roundtrip ("*[count(//*)]"); roundtrip ("(*)[count(//*)]"); } @Test public void testSequence() { roundtrip ("()"); roundtrip ("(a,b,c,d/e)"); roundtrip ("(1,3,'a',14.6,true())"); } @Test @Ignore public void testForwardAxes() { roundtrip ("*"); roundtrip ("./*"); roundtrip ("../*"); roundtrip (".//*"); roundtrip ("//*"); roundtrip ("following::*"); roundtrip ("following-sibling::*"); roundtrip ("self::*"); roundtrip ("attribute::*"); } @Test @Ignore public void testReverseAxes () { roundtrip ("ancestor::*"); roundtrip ("reverse(ancestor::*)"); roundtrip ("ancestor-or-self::*"); roundtrip ("./preceding::element()"); roundtrip ("preceding::*"); roundtrip ("preceding-sibling::*"); roundtrip ("//x/ancestor::*"); roundtrip ("../../x"); } @Test @Ignore public void testPositionalPredicates () { roundtrip ("preceding::*[1]"); roundtrip ("(preceding::*)[1]"); roundtrip ("*[1]"); roundtrip ("(*)[1]"); roundtrip ("descendant::*[3]"); roundtrip ("(descendant::*)[3]"); roundtrip ("ancestor::*[3]"); roundtrip ("(ancestor::*)[3]"); roundtrip ("descendant-or-self::*[last()]"); roundtrip ("(descendant-or-self::*)[last()]"); roundtrip ("ancestor-or-self::*[last()]"); roundtrip ("(ancestor-or-self::*)[last()]"); } private void roundtrip (String xpath) { XQueryExecutable query; Compiler compiler = eval.getCompiler(); try { query = compiler.getXQueryCompiler().compile(xpath); } catch (SaxonApiException e) { throw new LuxException(e); } SaxonTranslator translator = new SaxonTranslator(compiler.getProcessor().getUnderlyingConfiguration()); PathOptimizer optimizer = new PathOptimizer(compiler); XQuery originalQuery = translator.queryFor(query); XQuery optimizedQuery = optimizer.optimize(originalQuery); XQuery retranslated; try { retranslated = translator.queryFor(compiler.getXQueryCompiler().compile(optimizedQuery.getBody().toString())); } catch (SaxonApiException e) { throw new LuxException (e); } // compare the (compiled, translated, serialized) optimized query with the // (compiled, translated, serialized) original query assertEquals (xpath + " was not preserved", originalQuery.getBody().toString(), retranslated.getBody().toString()); } } /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */