package lux.junit; import static org.junit.Assert.*; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import lux.Compiler; import lux.Evaluator; import lux.QueryStats; import lux.XdmResultSet; import lux.exception.LuxException; import lux.support.SearchExtractor; import lux.xpath.AbstractExpression; import lux.xpath.ExpressionVisitorBase; import lux.xpath.FunCall; import lux.xpath.LiteralExpression; import lux.xquery.XQuery; import net.sf.saxon.expr.XPathContext; import net.sf.saxon.expr.sort.CodepointCollator; import net.sf.saxon.expr.sort.GenericAtomicComparer; import net.sf.saxon.functions.DeepEqual; import net.sf.saxon.s9api.Axis; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.trans.XPathException; import org.apache.commons.lang.StringUtils; import org.junit.Ignore; @Ignore class QueryTestCase { private final String name; private final String query; private final QueryTestResult expectedResult; QueryTestCase (String name, String query, QueryTestResult expected) { this.name = name; this.query = query; this.expectedResult = expected; } public XdmResultSet evaluate (Evaluator eval) { Compiler compiler = eval.getCompiler(); QueryStats stats = new QueryStats(); try { compiler.compile(query, null, null, stats); } catch (LuxException e) { if (! expectedResult.isError) { throw e; } if (! StringUtils.isEmpty(expectedResult.errorText)) { assertEquals (expectedResult.errorText, e.getMessage()); } return null; } if (expectedResult.isError) { fail ("expected exception not thrown"); } XQuery optimizedQuery = stats.optimizedXQuery; AbstractExpression ex = optimizedQuery.getBody(); String expectedOptimized = expectedResult.queryText; if (!StringUtils.isEmpty(expectedOptimized)) { String oq = optimizedQuery.toString().replaceAll ("{", "{").replaceAll("}", "}"); assertEquals (expectedOptimized, oq); } SearchExtractor extractor = new SearchExtractor(); ex.accept(extractor); List<XdmNode> queries = expectedResult.searchQueries; if (queries != null) { assertEquals ("wrong number of queries for " + query, queries.size(), extractor.getQueries().size()); for (int i = 0; i < queries.size(); i++) { AbstractExpression xmlQuery = extractor.getQueries().get(i).getQuery(); if (xmlQuery instanceof LiteralExpression) { continue; // we don't have a way to "unparse" these } XdmNode queryNode = (XdmNode) eval.build(new StringReader(xmlQuery.toString()), "query").axisIterator(Axis.CHILD).next(); // These are node trees, but let's use string comparison to display something meaningful to the user try { if (!compareNodes (eval, queries.get(i), queryNode)) { assertEquals (indent(queries.get(i).toString().trim()), indent(queryNode.toString().trim())); } } catch (XPathException e) { fail (e.getMessage()); } } if (queries.size() > 0) { if (expectedResult.resultType != null) { assertSame (expectedResult.resultType, extractor.getQueries().get(0).getResultType()); } } } // sort keys if (expectedResult.orderBy != null) { String[] sortFields = expectedResult.orderBy.split(","); SortExtractor sortExtractor = new SortExtractor(); ex.accept(sortExtractor); assertEquals ("incorrect number of sort fields:", sortFields.length, sortExtractor.sorts.size()); for (int i = 0; i < sortFields.length; i++) { assertEquals (sortFields[i], sortExtractor.sorts.get(i)); } } return null; } protected boolean compareNodes (Evaluator eval, XdmNode node1, XdmNode node2) throws XPathException { XPathContext context = eval.getCompiler().getProcessor().getUnderlyingConfiguration().getConversionContext(); return DeepEqual.deepEquals (node1.getUnderlyingNode().iterate(), node2.getUnderlyingNode().iterate(), new GenericAtomicComparer (CodepointCollator.getInstance(), context), context, DeepEqual.INCLUDE_PREFIXES | DeepEqual.EXCLUDE_WHITESPACE_TEXT_NODES | DeepEqual.COMPARE_STRING_VALUES ); } protected String indent (String s) { // attempt to normalize indentation with some heuristic String[] lines = s.split("\r?\n"); StringBuilder buf = new StringBuilder(); int lastOriginalIndent = 0; int curIndent = 0; for (String line : lines) { String unindented = line.replaceFirst("^\\s+", ""); int indentSize = line.length() - unindented.length(); if (indentSize < lastOriginalIndent) { curIndent -= 2; } else if (indentSize > lastOriginalIndent) { curIndent += 2; } lastOriginalIndent = indentSize; for (int i = 0; i < curIndent; i++) { buf.append (' '); } buf.append (unindented).append ('\n'); } return buf.toString(); } public String getName() { return name; } public String getQuery() { return query; } public QueryTestResult getExpectedResult() { return expectedResult; } static class SortExtractor extends ExpressionVisitorBase { ArrayList<String> sorts = new ArrayList<String>(); @Override public FunCall visit (FunCall funcall) { if (funcall.getName().equals (FunCall.LUX_SEARCH)) { if (funcall.getSubs().length >= 2) { AbstractExpression sortArg = funcall.getSubs()[1]; String s = ((LiteralExpression)sortArg).getValue().toString(); sorts.add(s); } } return funcall; } } }