From b2aca12c4354166ae69e98375b2d2497a9c873eb Mon Sep 17 00:00:00 2001 From: "Chris M. Hostetter" Date: Tue, 11 Apr 2006 04:36:46 +0000 Subject: [PATCH] SOLR-3 - a general purpose test harness, JUnit base class, and migrated version of SolrTest's newtest.txt to JUnit git-svn-id: https://svn.apache.org/repos/asf/incubator/solr/trunk@393142 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 58 +- .../apache/solr/request/SolrQueryRequest.java | 4 + .../solr/util/AbstractSolrTestCase.java | 250 ++++ .../org/apache/solr/util/TestHarness.java | 372 +++++ src/java/org/apache/solr/util/XML.java | 23 + .../apache/solr/BasicFunctionalityTest.java | 163 +++ .../org/apache/solr/ConvertedLegacyTest.java | 1296 +++++++++++++++++ src/test/org/apache/solr/SampleTest.java | 107 ++ 8 files changed, 2268 insertions(+), 5 deletions(-) create mode 100644 src/java/org/apache/solr/util/AbstractSolrTestCase.java create mode 100644 src/java/org/apache/solr/util/TestHarness.java create mode 100644 src/test/org/apache/solr/BasicFunctionalityTest.java create mode 100644 src/test/org/apache/solr/ConvertedLegacyTest.java create mode 100644 src/test/org/apache/solr/SampleTest.java diff --git a/build.xml b/build.xml index 7b99f2d1580..00f495debbb 100644 --- a/build.xml +++ b/build.xml @@ -29,6 +29,11 @@ + + + + + @@ -67,11 +72,11 @@ - + @@ -132,9 +137,11 @@ - - + depends="compileTests, junit" /> + + + @@ -143,7 +150,48 @@ + + + + + + + + + + + + + + + + + + + + Tests failed! + + + + + + + + + + + + diff --git a/src/java/org/apache/solr/request/SolrQueryRequest.java b/src/java/org/apache/solr/request/SolrQueryRequest.java index f8c5fd3435f..0a78a7bcfe6 100644 --- a/src/java/org/apache/solr/request/SolrQueryRequest.java +++ b/src/java/org/apache/solr/request/SolrQueryRequest.java @@ -25,6 +25,10 @@ import org.apache.solr.core.SolrCore; * @version $Id$ */ public interface SolrQueryRequest { + + /** All uses of this request are finished, resources can be freed */ + public void close(); + public String getParam(String name); public String getQueryString(); diff --git a/src/java/org/apache/solr/util/AbstractSolrTestCase.java b/src/java/org/apache/solr/util/AbstractSolrTestCase.java new file mode 100644 index 00000000000..34fc5c818e0 --- /dev/null +++ b/src/java/org/apache/solr/util/AbstractSolrTestCase.java @@ -0,0 +1,250 @@ + +package org.apache.solr.util; + +import org.apache.solr.request.*; +import org.apache.solr.util.TestHarness; + +import org.xml.sax.SAXException; +import junit.framework.TestCase; +import javax.xml.xpath.XPathExpressionException; + +import java.util.*; +import java.io.*; + +/** + * An Abstract base class that makes writing Solr JUnit tests "easier" + * + *

+ * Test classes that subclass this need only specify the path to the + * schema.xml file (:TODO: the solrconfig.xml as well) and write some + * testMethods. This class takes care of creating/destroying the index, + * and provides several assert methods to assist you. + *

+ * + * @see #setUp + * @see #tearDown + */ +public abstract class AbstractSolrTestCase extends TestCase { + + /** + * Harness initialized by initTestHarness. + * + *

+ * For use in test methods as needed. + *

+ */ + protected TestHarness h; + /** + * LocalRequestFactory initialized by initTestHarness using sensible + * defaults. + * + *

+ * For use in test methods as needed. + *

+ */ + protected TestHarness.LocalRequestFactory lrf; + + /** + * Subclasses must define this method to return the path of the + * schema.xml they wish to use. + */ + public abstract String getSchemaPath(); + + /** + * The directory used to story the index managed by the TestHarness h + */ + protected File dataDir; + + /** + * Initializes things your test might need + * + *
    + *
  • Creates a dataDir in the "java.io.tmpdir"
  • + *
  • initializes the TestHarness h using this data directory, and getSchemaPath()
  • + *
  • initializes the LocalRequestFactory lrf using sensible defaults.
  • + *
+ * + */ + public void setUp() throws Exception { + + dataDir = new File(System.getProperty("java.io.tmpdir") + + System.getProperty("file.separator") + + getClass().getName() + "-" + getName() + "-" + + System.currentTimeMillis()); + dataDir.mkdirs(); + h = new TestHarness(dataDir.getAbsolutePath(), getSchemaPath()); + lrf = h.getRequestFactory + ("standard",0,20,"version","2.0"); + + } + + /** + * Shuts down the test harness, and makes the best attempt possible + * to delete dataDir, unless the system property "solr.test.leavedatadir" + * is set. + */ + public void tearDown() throws Exception { + h.close(); + String skip = System.getProperty("solr.test.leavedatadir"); + if (null != skip && 0 != skip.trim().length()) { + System.err.println("NOTE: per solr.test.leavedatadir, dataDir will not be removed: " + dataDir.getAbsolutePath()); + } else { + if (!recurseDelete(dataDir)) { + System.err.println("!!!! WARNING: best effort to remove " + dataDir.getAbsolutePath() + " FAILED !!!!!"); + } + } + } + + /** Validates an update XML String is successful + */ + public void assertU(String update) { + assertU(null, update); + } + + /** Validates an update XML String is successful + */ + public void assertU(String message, String update) { + try { + String m = (null == message) ? "" : message + " "; + + String res = h.validateUpdate(update); + if (null != res) { + fail(m + "update was not successful: " + res); + } + } catch (SAXException e) { + throw new RuntimeException("Invalid XML", e); + } + } + + /** Validates a query matches some XPath test expressions and closes the query */ + public void assertQ(SolrQueryRequest req, String... tests) { + assertQ(null, req, tests); + } + + /** Validates a query matches some XPath test expressions and closes the query */ + public void assertQ(String message, SolrQueryRequest req, String... tests) { + try { + String m = (null == message) ? "" : message + " "; + String response = h.query(req); + String results = h.validateXPath(response, tests); + if (null != results) { + fail(m + "query failed XPath: " + results + + " xml response was: " + response); + } + } catch (XPathExpressionException e1) { + throw new RuntimeException("XPath is invalid", e1); + } catch (Exception e2) { + throw new RuntimeException("Exception during query", e2); + } + } + + /** + * @see TestHarness#optimize + */ + public String optimize(String... args) { + return h.optimize(); + } + /** + * @see TestHarness#commit + */ + public String commit(String... args) { + return h.commit(); + } + + /** + * Generates a simple <add><doc>... XML String with no options + * + * @param fieldsAndValues 0th and Even numbered args are fields names odds are field values. + * @see #add + * @see #doc + */ + public String adoc(String... fieldsAndValues) { + Doc d = doc(fieldsAndValues); + return add(d); + } + + /** + * Generates an <add><doc>... XML String with options + * on the add. + * + * @param doc the Document to add + * @param args 0th and Even numbered args are param names, Odds are param values. + * @see #add + * @see #doc + */ + public String add(Doc doc, String... args) { + try { + StringWriter r = new StringWriter(); + + // this is anoying + if (null == args || 0 == args.length) { + r.write(""); + r.write(doc.xml); + r.write(""); + } else { + XML.writeUnescapedXML(r, "add", doc.xml, (Object[])args); + } + + return r.getBuffer().toString(); + } catch (IOException e) { + throw new RuntimeException + ("this should never happen with a StringWriter", e); + } + } + + /** + * Generates a <delete>... XML string for an ID + * + * @see TestHarness#deleteById + */ + public String delI(String id) { + return h.deleteById(id); + } + /** + * Generates a <delete>... XML string for an query + * + * @see TestHarness#deleteByQuery + */ + public String delQ(String q) { + return h.deleteByQuery(q); + } + + /** + * Generates a simple <doc>... XML String with no options + * + * @param fieldsAndValues 0th and Even numbered args are fields names, Odds are field values. + * @see TestHarness#makeSimpleDoc + */ + public Doc doc(String... fieldsAndValues) { + Doc d = new Doc(); + d.xml = h.makeSimpleDoc(fieldsAndValues).toString(); + return d; + } + + /** + * Generates a SolrQueryRequest using the LocalRequestFactory + * @see #lrf + */ + public SolrQueryRequest req( String q ) { + return lrf.makeRequest(q); + } + + /** Neccessary to make method signatures un-ambiguous */ + public static class Doc { + public String xml; + public String toString() { return xml; } + } + + public static boolean recurseDelete(File f) { + if (f.isDirectory()) { + for (File sub : f.listFiles()) { + if (!recurseDelete(sub)) { + return false; + } + } + } + return f.delete(); + } + + +} diff --git a/src/java/org/apache/solr/util/TestHarness.java b/src/java/org/apache/solr/util/TestHarness.java new file mode 100644 index 00000000000..a1cf45e4072 --- /dev/null +++ b/src/java/org/apache/solr/util/TestHarness.java @@ -0,0 +1,372 @@ +/** + * Copyright 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.util; + +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.core.SolrCore; +import org.apache.solr.request.*; + +import org.xml.sax.SAXException; +import org.w3c.dom.Document; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathConstants; +import java.io.*; +import java.util.*; + + +/** + * This class provides a simple harness that may be usefull when + * writing testcasses. + * + *

+ * This class lives in the main source tree (and not in the test source + * tree) so that it will be included with even the most minimal solr + * distribution -- to encourage plugin writers to creat unit tests for their + * plugins. + * + * @author hossman + * @version $Id:$ + */ +public class TestHarness { + + private SolrCore core; + private XMLResponseWriter xmlwriter = new XMLResponseWriter(); + private XPath xpath = XPathFactory.newInstance().newXPath(); + private DocumentBuilder builder; + + /** + * @param dataDirectory path for index data, will not be cleaned up + * @param schemaFile path of schema file + */ + public TestHarness(String dataDirectory, String schemaFile) { + core = new SolrCore(dataDirectory, new IndexSchema(schemaFile)); + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /** + * Processes an "update" (add, commit or optimize) and + * returns the response as a String. + * + * @param xml The XML of the update + * @return The XML response to the update + */ + public String update(String xml) { + + StringReader req = new StringReader(xml); + StringWriter writer = new StringWriter(32000); + core.update(req, writer); + return writer.toString(); + + } + + + /** + * Validates that an "update" (add, commit or optimize) results in success. + * + * :TODO: currently only deals with one add/doc at a time, this will need changed if/when SOLR-2 is resolved + * + * @param xml The XML of the update + * @return null if succesful, otherwise the XML response to the update + */ + public String validateUpdate(String xml) throws SAXException { + try { + String res = update(xml); + String valid = validateXPath(res, "//result[@status=0]" ); + return (null == valid) ? null : res; + } catch (XPathExpressionException e) { + throw new RuntimeException + ("?!? static xpath has bug?", e); + } + } + + + /** + * Validates that an add of a single document results in success. + * + * @param fieldsAndValues Odds are field names, Evens are values + * @return null if succesful, otherwise the XML response to the update + * @see appendSimpleDoc + */ + public String validateAddDoc(String... fieldsAndValues) + throws XPathExpressionException, SAXException, IOException { + + StringBuffer buf = new StringBuffer(); + buf.append(""); + appendSimpleDoc(buf, fieldsAndValues); + buf.append(""); + + String res = update(buf.toString()); + String valid = validateXPath(res, "//result[@status=0]" ); + return (null == valid) ? null : res; + } + + + + /** + * Validates a "query" response against an array of XPath test strings + * + * @param req the Query to process + * @return null if all good, otherwise the first test that fails. + * @exception Exception any exception in the response. + * @exception IOException if there is a problem writing the XML + * @see LocalSolrQueryRequest + */ + public String validateQuery(SolrQueryRequest req, String... tests) + throws IOException, Exception { + + String res = query(req); + return validateXPath(res, tests); + } + + /** + * Processes a "query" using a user constructed SolrQueryRequest + * + * @param req the Query to process, will be closed. + * @return The XML response to the query + * @exception Exception any exception in the response. + * @exception IOException if there is a problem writing the XML + * @see LocalSolrQueryRequest + */ + public String query(SolrQueryRequest req) throws IOException, Exception { + + SolrQueryResponse rsp = new SolrQueryResponse(); + core.execute(req,rsp); + if (rsp.getException() != null) { + throw rsp.getException(); + } + + StringWriter writer = new StringWriter(32000); + xmlwriter.write(writer,req,rsp); + + req.close(); + + return writer.toString(); + } + + + /** + * A helper method which valides a String against an array of XPath test + * strings. + * + * @param xml The xml String to validate + * @param tests Array of XPath strings to test (in boolean mode) on the xml + * @return null if all good, otherwise the first test that fails. + */ + public String validateXPath(String xml, String... tests) + throws XPathExpressionException, SAXException { + + if (tests==null || tests.length == 0) return null; + + Document document=null; + try { + document = builder.parse(new ByteArrayInputStream + (xml.getBytes("UTF-8"))); + } catch (UnsupportedEncodingException e1) { + throw new RuntimeException("Totally weird UTF-8 exception", e1); + } catch (IOException e2) { + throw new RuntimeException("Totally weird io exception", e2); + } + + for (String xp : tests) { + xp=xp.trim(); + Boolean bool = (Boolean) xpath.evaluate(xp, document, + XPathConstants.BOOLEAN); + + if (!bool) { + return xp; + } + } + return null; + + } + + public SolrCore getCore() { + return core; + } + + /** + * Shuts down and frees any resources + */ + public void close() { + core.close(); + } + + /** + * A helper that adds an xml <doc> containing all of the + * fields and values specified (odds are fields, evens are values) + * to a StringBuffer. + */ + public void appendSimpleDoc(StringBuffer buf, String... fieldsAndValues) + throws IOException { + + buf.append(makeSimpleDoc(fieldsAndValues)); + } + /** + * A helper that creates an xml <doc> containing all of the + * fields and values specified + * + * @param fieldsAndValues 0 and Even numbered args are fields names odds are field values. + */ + public static StringBuffer makeSimpleDoc(String... fieldsAndValues) { + + try { + StringWriter w = new StringWriter(); + w.append(""); + for (int i = 0; i < fieldsAndValues.length; i+=2) { + XML.writeXML(w, "field", fieldsAndValues[i+1], "name", + fieldsAndValues[i]); + } + w.append(""); + return w.getBuffer(); + } catch (IOException e) { + throw new RuntimeException + ("this should never happen with a StringWriter", e); + } + } + + /** + * Generates a delete by query xml string + * @param q Query that has not already been xml escaped + */ + public static String deleteByQuery(String q) { + return delete("query", q); + } + /** + * Generates a delete by id xml string + * @param id ID that has not already been xml escaped + */ + public static String deleteById(String id) { + return delete("id", id); + } + + /** + * Generates a delete xml string + * @param val text that has not already been xml escaped + */ + private static String delete(String deltype, String val) { + try { + StringWriter r = new StringWriter(); + + r.write(""); + XML.writeXML(r, deltype, val); + r.write(""); + + return r.getBuffer().toString(); + } catch (IOException e) { + throw new RuntimeException + ("this should never happen with a StringWriter", e); + } + } + + /** + * Helper that returns an <optimize> String with + * optional key/val pairs. + * + * @param args 0 and Even numbered args are params, Odd numbered args are values. + */ + public static String optimize(String... args) { + return simpleTag("optimize", args); + } + + private static String simpleTag(String tag, String... args) { + try { + StringWriter r = new StringWriter(); + + // this is anoying + if (null == args || 0 == args.length) { + XML.writeXML(r, tag, null); + } else { + XML.writeXML(r, tag, null, (Object)args); + } + return r.getBuffer().toString(); + } catch (IOException e) { + throw new RuntimeException + ("this should never happen with a StringWriter", e); + } + } + + /** + * Helper that returns an <commit> String with + * optional key/val pairs. + * + * @param args 0 and Even numbered args are params, Odd numbered args are values. + */ + public static String commit(String... args) { + return simpleTag("commit", args); + } + + public LocalRequestFactory getRequestFactory(String qtype, + int start, + int limit) { + LocalRequestFactory f = new LocalRequestFactory(); + f.qtype = qtype; + f.start = start; + f.limit = limit; + return f; + } + + /** + * 0 and Even numbered args are keys, Odd numbered args are values. + */ + public LocalRequestFactory getRequestFactory(String qtype, + int start, int limit, + String... args) { + LocalRequestFactory f = getRequestFactory(qtype, start, limit); + for (int i = 0; i < args.length; i+=2) { + f.args.put(args[i], args[i+1]); + } + return f; + + } + + public LocalRequestFactory getRequestFactory(String qtype, + int start, int limit, + Map args) { + + LocalRequestFactory f = getRequestFactory(qtype, start, limit); + f.args.putAll(args); + return f; + } + + /** + * A Factory that generates LocalSolrQueryRequest objects using a + * specified set of default options. + */ + public class LocalRequestFactory { + public String qtype = "standard"; + public int start = 0; + public int limit = 1000; + public Map args = new HashMap(); + public LocalRequestFactory() { + } + public LocalSolrQueryRequest makeRequest(String q) { + return new LocalSolrQueryRequest(TestHarness.this.getCore(), + q, qtype, start, limit, args); + } + } +} diff --git a/src/java/org/apache/solr/util/XML.java b/src/java/org/apache/solr/util/XML.java index daf9d48a77b..02592358e9c 100644 --- a/src/java/org/apache/solr/util/XML.java +++ b/src/java/org/apache/solr/util/XML.java @@ -121,6 +121,29 @@ public class XML { } } + /** does NOT escape character data in val, must already be valid XML */ + public final static void writeUnescapedXML(Writer out, String tag, String val, Object... attrs) throws IOException { + out.write('<'); + out.write(tag); + for (int i=0; i"); + } else { + out.write('>'); + out.write(val); + out.write("'); + } + } + + /** escapes character data in val */ public final static void writeXML(Writer out, String tag, String val, Object... attrs) throws IOException { out.write('<'); out.write(tag); diff --git a/src/test/org/apache/solr/BasicFunctionalityTest.java b/src/test/org/apache/solr/BasicFunctionalityTest.java new file mode 100644 index 00000000000..0ebb3ea1c5a --- /dev/null +++ b/src/test/org/apache/solr/BasicFunctionalityTest.java @@ -0,0 +1,163 @@ +/** + * Copyright 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr; + +import org.apache.solr.request.*; +import org.apache.solr.util.*; + +/** + * Tests some basic functionality of Solr while demonstrating good + * Best Practices for using AbstractSolrTestCase + */ +public class BasicFunctionalityTest extends AbstractSolrTestCase { + + public String getSchemaPath() { return "solr/conf/schema.xml"; } + + public void setUp() throws Exception { + // if you override setUp or tearDown, you better call + // the super classes version + super.setUp(); + } + public void tearDown() throws Exception { + // if you override setUp or tearDown, you better call + // the super classes version + super.tearDown(); + + } + + public void testSomeStuff() throws Exception { + + assertQ("test query on empty index", + req("qlkciyopsbgzyvkylsjhchghjrdf") + ,"//result[@numFound='0']" + ); + + // test escaping of ";" + assertU("deleting 42 for no reason at all", + delI("42")); + assertU("adding doc#42", + adoc("id", "42", "val_s", "aa;bb")); + assertU("does commit work?", + commit()); + + assertQ("backslash escaping semicolon", + req("id:42 AND val_s:aa\\;bb") + ,"//*[@numFound='1']" + ,"//int[@name='id'][.='42']" + ); + + assertQ("quote escaping semicolon", + req("id:42 AND val_s:\"aa;bb\"") + ,"//*[@numFound='1']" + ,"//int[@name='id'][.='42']" + ); + + assertQ("no escaping semicolon", + req("id:42 AND val_s:aa") + ,"//*[@numFound='0']" + ); + + assertU(delI("42")); + assertU(commit()); + assertQ(req("id:42") + ,"//*[@numFound='0']" + ); + + // test allowDups default of false + + assertU(adoc("id", "42", "val_s", "AAA")); + assertU(adoc("id", "42", "val_s", "BBB")); + assertU(commit()); + assertQ(req("id:42") + ,"//*[@numFound='1']" + ,"//str[.='BBB']" + ); + assertU(adoc("id", "42", "val_s", "CCC")); + assertU(adoc("id", "42", "val_s", "DDD")); + assertU(commit()); + assertQ(req("id:42") + ,"//*[@numFound='1']" + ,"//str[.='DDD']" + ); + + // test deletes + String [] adds = new String[] { + add( doc("id","101"), "allowDups", "false" ), + add( doc("id","101"), "allowDups", "false" ), + add( doc("id","105"), "allowDups", "true" ), + add( doc("id","102"), "allowDups", "false" ), + add( doc("id","103"), "allowDups", "true" ), + add( doc("id","101"), "allowDups", "false" ), + }; + for (String a : adds) { + assertU(a, a); + } + assertU(commit()); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='4']" + ); + assertU(delI("102")); + assertU(commit()); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='3']" + ); + assertU(delI("105")); + assertU(commit()); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='2']" + ); + assertU(delQ("id:[100 TO 110]")); + assertU(commit()); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='0']" + ); + } + + + public void testMultipleUpdatesPerAdd() { + + // big freaking kludge since the response is currently not well formed. + String res = h.update("12"); + assertEquals("", res); + assertU(""); + assertQ(req("id:[0 TO 99]") + ,"//*[@numFound='2']" + ); + + } + + +// /** this doesn't work, but if it did, this is how we'd test it. */ +// public void testOverwriteFalse() { + +// assertU(adoc("id", "overwrite", "val_s", "AAA")); +// assertU(commit()); + +// assertU(add(doc("id", "overwrite", "val_s", "BBB") +// ,"allowDups", "false" +// ,"overwriteCommitted","false" +// ,"overwritePending","false" +// )); +// assertU(commit()); +// assertQ(req("id:overwrite") +// ,"//*[@numFound='1']" +// ,"//str[.='AAA']" +// ); +// } + + +} diff --git a/src/test/org/apache/solr/ConvertedLegacyTest.java b/src/test/org/apache/solr/ConvertedLegacyTest.java new file mode 100644 index 00000000000..3c68eafce10 --- /dev/null +++ b/src/test/org/apache/solr/ConvertedLegacyTest.java @@ -0,0 +1,1296 @@ +/** + * Copyright 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr; + +import org.apache.solr.request.*; +import org.apache.solr.util.*; + +import java.util.*; +import java.io.IOException; + +/** + * This tests was converted from a legacy testing system. + * + * it does not represent the best practices that should be used when + * writing Solr JUnit tests + */ +public class ConvertedLegacyTest extends AbstractSolrTestCase { + + public String getSchemaPath() { return "solr/conf/schema.xml"; } + + public void testABunchOfConvertedStuff() { + // these may be reused by things that need a special query + SolrQueryRequest req = null; + Map args = new HashMap(); + + // compact the index, keep things from getting out of hand + + assertU(""); + + // test query + + assertQ(req("qlkciyopsbgzyvkylsjhchghjrdf") + ,"//result[@numFound='0']" + ); + + // test escaping of ";" + + assertU("42"); + assertU("42aa;bb"); + assertU(""); + assertQ(req("id:42 AND val_s:aa\\;bb") + ,"//*[@numFound='1']" + ); + assertQ(req("id:42 AND val_s:\"aa;bb\"") + ,"//*[@numFound='1']" + ); + assertQ(req("id:42 AND val_s:\"aa\"") + ,"//*[@numFound='0']" + ); + + + + // test allowDups default of false + + assertU("42"); + assertU("42AAA"); + assertU("42BBB"); + assertU(""); + assertQ(req("id:42") + ,"//*[@numFound='1'] " + ,"//str[.='BBB']" + ); + assertU("42CCC"); + assertU("42DDD"); + assertU(""); + assertQ(req("id:42") + ,"//*[@numFound='1'] " + ,"//str[.='DDD']" + ); + assertU("42"); + + // test deletes + + assertU("id:[100 TO 110]"); + assertU("101"); + assertU("101"); + assertU("105"); + assertU("102"); + assertU("103"); + assertU("101"); + assertU(""); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='4']" + ); + assertU("102"); + assertU(""); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='3']" + ); + assertU("id:105"); + assertU(""); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='2']" + ); + assertU("id:[100 TO 110]"); + assertU(""); + assertQ(req("id:[100 TO 110]") + ,"//*[@numFound='0']" + ); + + // test range + + assertU("44"); + assertU("44apple"); + assertU("44banana"); + assertU("44pear"); + assertU(""); + assertQ(req("val_s:[a TO z]") + ,"//*[@numFound='3'] " + ,"*[count(//doc)=3] " + ,"//*[@start='0']" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 2, 5 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=1] " + ,"*//doc[1]/str[.='pear'] " + ,"//*[@start='2']" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 3, 5 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 4, 5 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 25, 5 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 0, 1 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=1] " + ,"*//doc[1]/str[.='apple']" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 0, 2 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=2] " + ,"*//doc[2]/str[.='banana']" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 1, 1 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=1] " + ,"*//doc[1]/str[.='banana']" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 3, 1 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 4, 1 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 1, 0 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z]", + "standard", 0, 0 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z];val_s asc", + "standard", 0, 0 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "val_s:[a TO z];val_s desc", + "standard", 0, 0 , args); + assertQ(req + ,"//*[@numFound='3'] " + ,"*[count(//doc)=0]" + ); + assertQ(req("val_s:[a TO b]") + ,"//*[@numFound='1']" + ); + assertQ(req("val_s:[a TO cat]") + ,"//*[@numFound='2']" + ); + assertQ(req("val_s:[a TO *]") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:[* TO z]") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:[* TO *]") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:[apple TO pear]") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:[bear TO boar]") + ,"//*[@numFound='0']" + ); + assertQ(req("val_s:[a TO a]") + ,"//*[@numFound='0']" + ); + assertQ(req("val_s:[apple TO apple]") + ,"//*[@numFound='1']" + ); + assertQ(req("val_s:{apple TO pear}") + ,"//*[@numFound='1']" + ); + assertQ(req("val_s:{a TO z}") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:{* TO *}") + ,"//*[@numFound='3']" + ); + // test rangequery within a boolean query + + assertQ(req("id:44 AND val_s:[a TO z]") + ,"//*[@numFound='3']" + ); + assertQ(req("id:44 OR val_s:[a TO z]") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:[a TO b] OR val_s:[b TO z]") + ,"//*[@numFound='3']" + ); + assertQ(req("+val_s:[a TO b] -val_s:[b TO z]") + ,"//*[@numFound='1']" + ); + assertQ(req("-val_s:[a TO b] +val_s:[b TO z]") + ,"//*[@numFound='2']" + ); + assertQ(req("val_s:[a TO c] AND val_s:[apple TO z]") + ,"//*[@numFound='2']" + ); + assertQ(req("val_s:[a TO c] AND val_s:[a TO apple]") + ,"//*[@numFound='1']" + ); + assertQ(req("id:44 AND (val_s:[a TO c] AND val_s:[a TO apple])") + ,"//*[@numFound='1']" + ); + assertQ(req("(val_s:[apple TO apple] OR val_s:[a TO c]) AND (val_s:[b TO c] OR val_s:[b TO b])") + ,"//*[@numFound='1'] " + ,"//str[.='banana']" + ); + assertQ(req("(val_s:[apple TO apple] AND val_s:[a TO c]) OR (val_s:[p TO z] AND val_s:[a TO z])") + ,"//*[@numFound='2'] " + ,"//str[.='apple'] " + ,"//str[.='pear']" + ); + + // check for docs that appear more than once in a range + + assertU("44applebanana"); + assertU(""); + assertQ(req("val_s:[* TO *] OR val_s:[* TO *]") + ,"//*[@numFound='4']" + ); + assertQ(req("val_s:[* TO *] AND val_s:[* TO *]") + ,"//*[@numFound='4']" + ); + assertQ(req("val_s:[* TO *]") + ,"//*[@numFound='4']" + ); + + + // 44 + + assertU("44red riding hood"); + assertU(""); + assertQ(req("id:44 AND red") + ,"//@numFound[.='1'] " + ,"*[count(//doc)=1]" + ); + assertQ(req("id:44 AND ride") + ,"//@numFound[.='1']" + ); + assertQ(req("id:44 AND blue") + ,"//@numFound[.='0']" + ); + + // allow duplicates + + assertU("44"); + assertU("44red riding hood"); + assertU("44big bad wolf"); + assertU(""); + assertQ(req("id:44") + ,"//@numFound[.='2']" + ); + assertQ(req("id:44 AND red") + ,"//@numFound[.='1'] " + ,"*[count(//doc)=1]" + ); + assertQ(req("id:44 AND wolf") + ,"//@numFound[.='1'] " + ,"*[count(//doc)=1]" + ); + assertQ(req("+id:44 red wolf") + ,"//@numFound[.='2']" + ); + + // test removal of multiples w/o adding anything else + + assertU("44"); + assertU(""); + assertQ(req("id:44") + ,"//@numFound[.='0']" + ); + + // untokenized string type + + assertU("44"); + assertU("44and a 10.4 ?"); + assertU(""); + assertQ(req("id:44") + ,"//str[.='and a 10.4 ?']" + ); + assertU("44"); + assertU("44abc123"); + assertU(""); + // TODO: how to search for something with spaces.... + + assertQ(req("sind:abc123") + ,"//@numFound[.='1'] " + ,"*[count(//@name[.='sind'])=0] " + ,"*[count(//@name[.='id'])=1]" + ); + + assertU("44"); + assertU("44"); + assertU("44abc123"); + assertU(""); + // TODO: how to search for something with spaces.... + + assertQ(req("sindsto:abc123") + ,"//str[.='abc123']" + ); + + // test output of multivalued fields + + assertU("44"); + assertU("44yonik3yonik4"); + assertU(""); + assertQ(req("id:44") + ,"//arr[@name='title'][./str='yonik3' and ./str='yonik4'] " + ,"*[count(//@name[.='title'])=1]" + ); + assertQ(req("title:yonik3") + ,"//@numFound[.>'0']" + ); + assertQ(req("title:yonik4") + ,"//@numFound[.>'0']" + ); + assertQ(req("title:yonik5") + ,"//@numFound[.='0']" + ); + assertU("title:yonik4"); + assertU(""); + assertQ(req("id:44") + ,"//@numFound[.='0']" + ); + + + // not visible until commit + + assertU("44"); + assertU(""); + assertU("44"); + assertQ(req("id:44") + ,"//@numFound[.='0']" + ); + assertU(""); + assertQ(req("id:44") + ,"//@numFound[.='1']" + ); + + // test configurable stop words + + assertU("44"); + assertU("44world stopworda view"); + assertU(""); + assertQ(req("+id:44 +teststop:world") + ,"//@numFound[.='1']" + ); + assertQ(req("teststop:stopworda") + ,"//@numFound[.='0']" + ); + + // test ignoreCase stop words + + assertU("44"); + assertU("44world AnD view"); + assertU(""); + assertQ(req("+id:44 +stopfilt:world") + ,"//@numFound[.='1']" + ); + assertQ(req("stopfilt:\"and\"") + ,"//@numFound[.='0']" + ); + assertQ(req("stopfilt:\"AND\"") + ,"//@numFound[.='0']" + ); + assertQ(req("stopfilt:\"AnD\"") + ,"//@numFound[.='0']" + ); + + // test dynamic field types + + assertU("44"); + assertU("4451778cats"); + assertU(""); + // test if the dyn fields got added + + assertQ(req("id:44") + ,"*[count(//doc/*)>=3] " + ,"//int[@name='gack_i'][.='51778'] " + ,"//str[@name='t_name'][.='cats']" + ); + // now test if we can query by a dynamic field (requires analyzer support) + + assertQ(req("t_name:cat") + ,"//str[@name='t_name' and .='cats']" + ); + // check that deleteByQuery works for dynamic fields + + assertU("t_name:cat"); + assertU(""); + assertQ(req("t_name:cat") + ,"//@numFound[.='0']" + ); + + // test that longest dynamic field match happens first + + assertU("44mystr12321"); + assertU(""); + assertQ(req("id:44") + ,"//str[@name='xaa'][.='mystr'] " + ,"//int[@name='xaaa'][.='12321']" + ); + + + // test integer ranges and sorting + + assertU("44"); + assertU("441234567890"); + assertU("4410"); + assertU("441"); + assertU("442"); + assertU("4415"); + assertU("44-1"); + assertU("44-987654321"); + assertU("442147483647"); + assertU("44-2147483648"); + assertU("440"); + assertU(""); + assertQ(req("id:44") + ,"*[count(//doc)=10]" + ); + assertQ(req("num_i:2147483647") + ,"//@numFound[.='1'] " + ,"//int[.='2147483647']" + ); + assertQ(req("num_i:\"-2147483648\"") + ,"//@numFound[.='1'] " + ,"//int[.='-2147483648']" + ); + assertQ(req("id:44;num_i asc;") + ,"//doc[1]/int[.='-2147483648'] " + ,"//doc[last()]/int[.='2147483647']" + ); + assertQ(req("id:44;num_i desc;") + ,"//doc[1]/int[.='2147483647'] " + ,"//doc[last()]/int[.='-2147483648']" + ); + assertQ(req("num_i:[0 TO 9]") + ,"*[count(//doc)=3]" + ); + assertQ(req("num_i:[-2147483648 TO 2147483647]") + ,"*[count(//doc)=10]" + ); + assertQ(req("num_i:[-10 TO -1]") + ,"*[count(//doc)=1]" + ); + + // test long ranges and sorting + + assertU("44"); + assertU("441234567890"); + assertU("4410"); + assertU("441"); + assertU("442"); + assertU("4415"); + assertU("44-1"); + assertU("44-987654321"); + assertU("449223372036854775807"); + assertU("44-9223372036854775808"); + assertU("440"); + assertU(""); + assertQ(req("id:44") + ,"*[count(//doc)=10]" + ); + assertQ(req("num_l:9223372036854775807") + ,"//@numFound[.='1'] " + ,"//long[.='9223372036854775807']" + ); + assertQ(req("num_l:\"-9223372036854775808\"") + ,"//@numFound[.='1'] " + ,"//long[.='-9223372036854775808']" + ); + assertQ(req("id:44;num_l asc;") + ,"//doc[1]/long[.='-9223372036854775808'] " + ,"//doc[last()]/long[.='9223372036854775807']" + ); + assertQ(req("id:44;num_l desc;") + ,"//doc[1]/long[.='9223372036854775807'] " + ,"//doc[last()]/long[.='-9223372036854775808']" + ); + assertQ(req("num_l:[-1 TO 9]") + ,"*[count(//doc)=4]" + ); + assertQ(req("num_l:[-9223372036854775808 TO 9223372036854775807]") + ,"*[count(//doc)=10]" + ); + assertQ(req("num_l:[-10 TO -1]") + ,"*[count(//doc)=1]" + ); + + // test binary float ranges and sorting + + assertU("44"); + assertU("441.4142135"); + assertU("44Infinity"); + assertU("44-Infinity"); + assertU("44NaN"); + assertU("442"); + assertU("44-1"); + assertU("44-987654321"); + assertU("44-999999.99"); + assertU("44-1e20"); + assertU("440"); + assertU(""); + assertQ(req("id:44") + ,"*[count(//doc)=10]" + ); + assertQ(req("num_f:Infinity") + ,"//@numFound[.='1'] " + ,"//float[.='Infinity']" + ); + assertQ(req("num_f:\"-Infinity\"") + ,"//@numFound[.='1'] " + ,"//float[.='-Infinity']" + ); + assertQ(req("num_f:\"NaN\"") + ,"//@numFound[.='1'] " + ,"//float[.='NaN']" + ); + assertQ(req("num_f:\"-1e20\"") + ,"//@numFound[.='1']" + ); + assertQ(req("id:44;num_f asc;") + ,"//doc[1]/float[.='-Infinity'] " + ,"//doc[last()]/float[.='NaN']" + ); + assertQ(req("id:44;num_f desc;") + ,"//doc[1]/float[.='NaN'] " + ,"//doc[last()]/float[.='-Infinity']" + ); + assertQ(req("num_f:[-1 TO 2]") + ,"*[count(//doc)=4]" + ); + assertQ(req("num_f:[-Infinity TO Infinity]") + ,"*[count(//doc)=9]" + ); + + + + // test binary double ranges and sorting + + assertU("44"); + assertU("441.4142135"); + assertU("44Infinity"); + assertU("44-Infinity"); + assertU("44NaN"); + assertU("442"); + assertU("44-1"); + assertU("441e-100"); + assertU("44-999999.99"); + assertU("44-1e100"); + assertU("440"); + assertU(""); + assertQ(req("id:44") + ,"*[count(//doc)=10]" + ); + assertQ(req("num_d:Infinity") + ,"//@numFound[.='1'] " + ,"//double[.='Infinity']" + ); + assertQ(req("num_d:\"-Infinity\"") + ,"//@numFound[.='1'] " + ,"//double[.='-Infinity']" + ); + assertQ(req("num_d:\"NaN\"") + ,"//@numFound[.='1'] " + ,"//double[.='NaN']" + ); + assertQ(req("num_d:\"-1e100\"") + ,"//@numFound[.='1']" + ); + assertQ(req("num_d:\"1e-100\"") + ,"//@numFound[.='1']" + ); + assertQ(req("id:44;num_d asc;") + ,"//doc[1]/double[.='-Infinity'] " + ,"//doc[last()]/double[.='NaN']" + ); + assertQ(req("id:44;num_d desc;") + ,"//doc[1]/double[.='NaN'] " + ,"//doc[last()]/double[.='-Infinity']" + ); + assertQ(req("num_d:[-1 TO 2]") + ,"*[count(//doc)=5]" + ); + assertQ(req("num_d:[-Infinity TO Infinity]") + ,"*[count(//doc)=9]" + ); + + + // test sorting on multiple fields + + assertU("44"); + assertU("4410"); + assertU("441100"); + assertU("44-1"); + assertU("4415"); + assertU("44150"); + assertU("440"); + assertU(""); + assertQ(req("id:44") + ,"*[count(//doc)=6]" + ); + + assertQ(req("id:44; a_i asc,b_i desc") + ,"*[count(//doc)=6] " + ,"//doc[3]/int[.='100'] " + ,"//doc[4]/int[.='50']" + ); + assertQ(req("id:44;a_i asc , b_i asc;") + ,"*[count(//doc)=6] " + ,"//doc[3]/int[.='50'] " + ,"//doc[4]/int[.='100']" + ); + assertQ(req("id:44;a_i asc;") + ,"*[count(//doc)=6] " + ,"//doc[1]/int[.='-1'] " + ,"//doc[last()]/int[.='15']" + ); + assertQ(req("id:44;a_i asc , score top;") + ,"*[count(//doc)=6] " + ,"//doc[1]/int[.='-1'] " + ,"//doc[last()]/int[.='15']" + ); + assertQ(req("id:44; score top , a_i top, b_i bottom ;") + ,"*[count(//doc)=6] " + ,"//doc[last()]/int[.='-1'] " + ,"//doc[1]/int[.='15'] " + ,"//doc[3]/int[.='50'] " + ,"//doc[4]/int[.='100']" + ); + + + // test sorting with some docs missing the sort field + + assertU("id_i:[1000 TO 1010]"); + assertU("10001"); + assertU("100110"); + assertU("10021100"); + assertU("1003-1"); + assertU("100415"); + assertU("1005150"); + assertU("10060"); + assertU(""); + assertQ(req("id_i:[1000 TO 1010]") + ,"*[count(//doc)=7]" + ); + assertQ(req("id_i:[1000 TO 1010]; b_i asc") + ,"*[count(//doc)=7] " + ,"//doc[1]/int[.='50'] " + ,"//doc[2]/int[.='100']" + ); + assertQ(req("id_i:[1000 TO 1010]; b_i desc") + ,"*[count(//doc)=7] " + ,"//doc[1]/int[.='100'] " + ,"//doc[2]/int[.='50']" + ); + assertQ(req("id_i:[1000 TO 1010]; a_i asc,b_i desc") + ,"*[count(//doc)=7] " + ,"//doc[3]/int[.='100'] " + ,"//doc[4]/int[.='50'] " + ,"//doc[5]/int[.='1000']" + ); + assertQ(req("id_i:[1000 TO 1010]; a_i asc,b_i asc") + ,"*[count(//doc)=7] " + ,"//doc[3]/int[.='50'] " + ,"//doc[4]/int[.='100'] " + ,"//doc[5]/int[.='1000']" + ); + + + // test prefix query + + assertU("val_s:[* TO *]"); + assertU("100apple"); + assertU("101banana"); + assertU("102apple"); + assertU("103pearing"); + assertU("104pear"); + assertU("105appalling"); + assertU("106pearson"); + assertU("107port"); + assertU(""); + + assertQ(req("val_s:a*") + ,"//*[@numFound='3']" + ); + assertQ(req("val_s:p*") + ,"//*[@numFound='4']" + ); + // val_s:* %//*[@numFound="8"] + + + assertU("id:[100 TO 110]"); + + // test copyField functionality + + assertU("42How Now4 brown Cows"); + assertU(""); + assertQ(req("id:42 AND title:Now") + ,"*[count(//doc)=0]" + ); + assertQ(req("id:42 AND title_lettertok:Now") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND title:cow") + ,"*[count(//doc)=0]" + ); + assertQ(req("id:42 AND title_stemmed:cow") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND text:cow") + ,"*[count(//doc)=1]" + ); + + // test slop + + assertU("42foo bar"); + assertU(""); + assertQ(req("id:42 AND text:\"foo bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND text:\"foo\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND text:\"bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND text:\"bar foo\"") + ,"*[count(//doc)=0]" + ); + assertQ(req("id:42 AND text:\"bar foo\"~2") + ,"*[count(//doc)=1]" + ); + + + // intra-word delimiter testing (WordDelimiterFilter) + + assertU("42foo bar"); + assertU(""); + assertQ(req("id:42 AND subword:\"foo bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"foo\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"bar foo\"") + ,"*[count(//doc)=0]" + ); + assertQ(req("id:42 AND subword:\"bar foo\"~2") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"foo/bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"foobar\"") + ,"*[count(//doc)=0]" + ); + + assertU("42foo-bar"); + assertU(""); + assertQ(req("id:42 AND subword:\"foo bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"foo\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"bar foo\"") + ,"*[count(//doc)=0]" + ); + assertQ(req("id:42 AND subword:\"bar foo\"~2") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"foo/bar\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:foobar") + ,"*[count(//doc)=1]" + ); + + assertU("42Canon PowerShot SD500 7MP"); + assertU(""); + assertQ(req("id:42 AND subword:\"power-shot\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"power shot sd 500\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"powershot\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"SD-500\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"SD500\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"SD500-7MP\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"PowerShotSD500-7MP\"") + ,"*[count(//doc)=1]" + ); + + assertU("42Wi-Fi"); + assertU(""); + assertQ(req("id:42 AND subword:wifi") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:wi+=fi") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:wi+=fi") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:WiFi") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"wi fi\"") + ,"*[count(//doc)=1]" + ); + + assertU("42'I.B.M' A's,B's,C's"); + assertU(""); + assertQ(req("id:42 AND subword:\"'I.B.M.'\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:I.B.M") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:IBM") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:I--B--M") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"I B M\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:IBM's") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:IBM'sx") + ,"*[count(//doc)=0]" + ); + + // this one fails since IBM and ABC are separated by two tokens + + // id:42 AND subword:IBM's-ABC's %*[count(//doc)=1] + + assertQ(req("id:42 AND subword:\"IBM's-ABC's\"~2") + ,"*[count(//doc)=1]" + ); + + assertQ(req("id:42 AND subword:\"A's B's-C's\"") + ,"*[count(//doc)=1]" + ); + + assertU("42Sony KDF-E50A10"); + assertU(""); + + // check for exact match: + + // Sony KDF E/KDFE 50 A 10 (this is how it's indexed) + + // Sony KDF E 50 A 10 (and how it's queried) + + assertQ(req("id:42 AND subword:\"Sony KDF-E50A10\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:10") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:Sony") + ,"*[count(//doc)=1]" + ); + + // this one fails without slop since Sony and KDFE have a token inbetween + + // id:42 AND subword:SonyKDFE50A10 %*[count(//doc)=1] + + assertQ(req("id:42 AND subword:\"SonyKDFE50A10\"~10") + ,"*[count(//doc)=1]" + ); + + assertQ(req("id:42 AND subword:\"Sony KDF E-50-A-10\"") + ,"*[count(//doc)=1]" + ); + + assertU("42http://www.yahoo.com"); + assertU(""); + assertQ(req("id:42 AND subword:yahoo") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:www.yahoo.com") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:http://www.yahoo.com") + ,"*[count(//doc)=1]" + ); + + assertU("42--Q 1-- W2 E-3 Ok xY 4R 5-T *6-Y- 7-8-- 10A-B"); + assertU(""); + assertQ(req("id:42 AND subword:Q") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:1") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"w 2\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"e 3\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"o k\"") + ,"*[count(//doc)=0]" + ); + assertQ(req("id:42 AND subword:\"ok\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"x y\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"xy\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"4 r\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"5 t\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"5 t\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"6 y\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"7 8\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"78\"") + ,"*[count(//doc)=1]" + ); + assertQ(req("id:42 AND subword:\"10 A+B\"") + ,"*[count(//doc)=1]" + ); + + assertU("42FooBarBaz"); + assertU("42FooBar10"); + assertU("4210FooBar"); + assertU("42BAZ"); + assertU("4210"); + assertU("42Mark, I found what's the problem! It turns to be from the latest schema. I found tons of exceptions in the resin.stdout that prevented the builder from performing. It's all coming from the WordDelimiterFilter which was just added to the latest schema: [2005-08-29 15:11:38.375] java.lang.IndexOutOfBoundsException: Index: 3, Size: 3 673804 [2005-08-29 15:11:38.375] at java.util.ArrayList.RangeCheck(ArrayList.java:547) 673805 [2005-08-29 15:11:38.375] at java.util.ArrayList.get(ArrayList.java:322) 673806 [2005-08-29 15:11:38.375] at solr.analysis.WordDelimiterFilter.addCombos(WordDelimiterFilter.java:349) 673807 [2005-08-29 15:11:38.375] at solr.analysis.WordDelimiterFilter.next(WordDelimiterFilter.java:325) 673808 [2005-08-29 15:11:38.375] at org.apache.lucene.analysis.LowerCaseFilter.next(LowerCaseFilter.java:32) 673809 [2005-08-29 15:11:38.375] at org.apache.lucene.analysis.StopFilter.next(StopFilter.java:98) 673810 [2005-08-29 15:11:38.375] at solr.EnglishPorterFilter.next(TokenizerFactory.java:163) 673811 [2005-08-29 15:11:38.375] at org.apache.lucene.index.DocumentWriter.invertDocument(DocumentWriter.java:143) 673812 [2005-08-29 15:11:38.375] at org.apache.lucene.index.DocumentWriter.addDocument(DocumentWriter.java:81) 673813 [2005-08-29 15:11:38.375] at org.apache.lucene.index.IndexWriter.addDocument(IndexWriter.java:307) 673814 [2005-08-29 15:11:38.375] at org.apache.lucene.index.IndexWriter.addDocument(IndexWriter.java:294) 673815 [2005-08-29 15:11:38.375] at solr.DirectUpdateHandler2.doAdd(DirectUpdateHandler2.java:170) 673816 [2005-08-29 15:11:38.375] at solr.DirectUpdateHandler2.overwriteBoth(DirectUpdateHandler2.java:317) 673817 [2005-08-29 15:11:38.375] at solr.DirectUpdateHandler2.addDoc(DirectUpdateHandler2.java:191) 673818 [2005-08-29 15:11:38.375] at solr.SolrCore.update(SolrCore.java:795) 673819 [2005-08-29 15:11:38.375] at solrserver.SolrServlet.doPost(SolrServlet.java:71) 673820 [2005-08-29 15:11:38.375] at javax.servlet.http.HttpServlet.service(HttpServlet.java:154) 673821 [2005-08-29 15:11:38.375] at javax.servlet.http.HttpServlet.service(HttpServlet.java:92) 673822 [2005-08-29 15:11:38.375] at com.caucho.server.dispatch.ServletFilterChain.doFilter(ServletFilterChain.java:99) 673823 [2005-08-29 15:11:38.375] at com.caucho.server.cache.CacheFilterChain.doFilter(CacheFilterChain.java:188) 673824 [2005-08-29 15:11:38.375] at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:163) 673825 [2005-08-29 15:11:38.375] at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:208) 673826 [2005-08-29 15:11:38.375] at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:259) 673827 [2005-08-29 15:11:38.375] at com.caucho.server.port.TcpConnection.run(TcpConnection.java:363) 673828 [2005-08-29 15:11:38.375] at com.caucho.util.ThreadPool.runTasks(ThreadPool.java:490) 673829 [2005-08-29 15:11:38.375] at com.caucho.util.ThreadPool.run(ThreadPool.java:423) 673830 [2005-08-29 15:11:38.375] at java.lang.Thread.run(Thread.java:595) With the previous schema I'm able to perform a successful full build: http://c12-ssa-dev40-so-mas1.cnet.com:5078/select/?stylesheet=q=docTypeversion=2.0start=0rows=10indent=on Do you want to rollback to the previous schema version"); + + + // + + assertU("44"); + assertU("44Yoniktrue10000000000software engineer1e1003.14159622005-03-18T01:14:34Z1.414213562.999"); + assertU(""); + assertQ(req("id:44") + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","fname_s,arr_f "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44", + "standard", 0, 10, args); + assertQ(req + ,"//str[.='Yonik'] " + ,"//float[.='1.4142135']" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl"," "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44", + "standard", 0, 10, args); + assertQ(req + ,"//str[.='Yonik'] " + ,"//float[.='1.4142135']" + ); + + // test addition of score field + + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","score "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44", + "standard", 0, 10, args); + assertQ(req + ,"//str[.='Yonik'] " + ,"//float[.='1.4142135'] " + ,"//float[@name='score'] " + ,"*[count(//doc/*)=10]" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","*,score "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44", + "standard", 0, 10, args); + assertQ(req + ,"//str[.='Yonik'] " + ,"//float[.='1.4142135'] " + ,"//float[@name='score'] " + ,"*[count(//doc/*)=10]" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","* "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44", + "standard", 0, 10, args); + assertQ(req + ,"//str[.='Yonik'] " + ,"//float[.='1.4142135'] " + ,"*[count(//doc/*)>=9]" + ); + + // test maxScore + + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","score "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44", + "standard", 0, 10, args); + assertQ(req + ,"//result[@maxScore>0]" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","score "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44;id desc;", + "standard", 0, 10, args); + assertQ(req + ,"//result[@maxScore>0]" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","score "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44;", + "standard", 0, 10, args); + assertQ(req + ,"//@maxScore = //doc/float[@name='score']" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","score "); + req = new LocalSolrQueryRequest(h.getCore(), "id:44;id desc;", + "standard", 0, 10, args); + assertQ(req + ,"//@maxScore = //doc/float[@name='score']" + ); + args = new HashMap(); + args.put("version","2.0"); + args.put("fl","score"); + req = new LocalSolrQueryRequest(h.getCore(), "id:44;id desc;", + "standard", 0, 0 , args); + assertQ(req + ,"//result[@maxScore>0]" + ); + + + // test schema field attribute inheritance and overriding + + assertU("44"); + assertU("44hi"); + assertU(""); + assertQ(req("id:44") + ,"//*[@name='shouldbestored']" + ); + assertQ(req("+id:44 +shouldbestored:hi") + ,"//*[@numFound='1']" + ); + + assertU("44"); + assertU("44hi"); + assertU(""); + assertQ(req("id:44") + ,"not(//*[@name='shouldbeunstored'])" + ); + assertQ(req("+id:44 +shouldbeunstored:hi") + ,"//*[@numFound='1']" + ); + + assertU("44"); + assertU("44hi"); + assertU(""); + assertQ(req("id:44") + ,"//*[@name='shouldbeunindexed']" + ); + // this should result in an error... how to check for that? + + // +id:44 +shouldbeunindexed:hi %//*[@numFound="0"] + + + // test spaces between XML elements because that can introduce extra XML events that + + // can mess up parsing (and it has in the past) + + assertU(" 44 "); + assertU(" 44 hi "); + assertU(" "); + + // test adding multiple docs per add command + + // assertU("id:[0 TO 99]"); + // assertU("12"); + // assertU(""); + // assertQ(req("id:[0 TO 99]") + // ,"//*[@numFound='2']" + // ); + + // test synonym filter + + assertU("id:[10 TO 100]"); + assertU("10a"); + assertU("11b"); + assertU("12c"); + assertU("13foo"); + assertU(""); + assertQ(req("id:10 AND syn:a") + ,"//*[@numFound='1']" + ); + assertQ(req("id:10 AND syn:aa") + ,"//*[@numFound='1']" + ); + assertQ(req("id:11 AND syn:b") + ,"//*[@numFound='1']" + ); + assertQ(req("id:11 AND syn:b1") + ,"//*[@numFound='1']" + ); + assertQ(req("id:11 AND syn:b2") + ,"//*[@numFound='1']" + ); + assertQ(req("id:12 AND syn:c") + ,"//*[@numFound='1']" + ); + assertQ(req("id:12 AND syn:c1") + ,"//*[@numFound='1']" + ); + assertQ(req("id:12 AND syn:c2") + ,"//*[@numFound='1']" + ); + assertQ(req("id:13 AND syn:foo") + ,"//*[@numFound='1']" + ); + assertQ(req("id:13 AND syn:bar") + ,"//*[@numFound='1']" + ); + assertQ(req("id:13 AND syn:baz") + ,"//*[@numFound='1']" + ); + + + // test position increment gaps between field values + + assertU("44"); + assertU("45"); + assertU("44aa bb ccdd ee ff"); + assertU("45aa bb ccdd ee ff"); + assertU(""); + assertQ(req("+id:44 +textgap:\"aa bb cc\"") + ,"//*[@numFound='1']" + ); + assertQ(req("+id:44 +textgap:\"dd ee ff\"") + ,"//*[@numFound='1']" + ); + assertQ(req("+id:44 +textgap:\"cc dd\"") + ,"//*[@numFound='0']" + ); + assertQ(req("+id:44 +textgap:\"cc dd\"~100") + ,"//*[@numFound='1']" + ); + assertQ(req("+id:44 +textgap:\"bb cc dd ee\"~90") + ,"//*[@numFound='0']" + ); + assertQ(req("+id:44 +textgap:\"bb cc dd ee\"~100") + ,"//*[@numFound='1']" + ); + assertQ(req("+id:45 +text:\"cc dd\"") + ,"//*[@numFound='1']" + ); + + + // trigger output of custom value test + + args = new HashMap(); + args.put("version","2.0"); + req = new LocalSolrQueryRequest(h.getCore(), "values", + "test", 0, 10, args); + assertQ(req + ); + + } +} diff --git a/src/test/org/apache/solr/SampleTest.java b/src/test/org/apache/solr/SampleTest.java new file mode 100644 index 00000000000..5b481e10cf0 --- /dev/null +++ b/src/test/org/apache/solr/SampleTest.java @@ -0,0 +1,107 @@ +/** + * Copyright 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr; + +import org.apache.solr.request.*; +import org.apache.solr.util.*; + +import java.util.*; +import java.io.IOException; + +/** + * This is an example of how to write a JUnit tests for Solr using the + * AbstractSolrTestCase + */ +public class SampleTest extends AbstractSolrTestCase { + + /** + * All subclasses of AbstractSolrTestCase must define this method + */ + public String getSchemaPath() { return "solr/conf/schema.xml"; } + + /** + * Demonstration of some of the simple ways to use the base class + */ + public void testSimple() { + + assertU("Simple assertion that adding a document works", + adoc("id", "4055", + "subject", "Hoss the Hoss man Hostetter")); + + /* alternate syntax, no label */ + assertU(adoc("id", "4056", + "subject", "Some Other Guy")); + + assertU(commit()); + assertU(optimize()); + + assertQ("couldn't find subject hoss", + req("subject:Hoss") + ,"//result[@numFound=1]" + ,"//int[@name='id'][.='4055']" + ); + } + + /** + * Demonstration of some of the more complex ways to use the base class + */ + public void testAdvanced() throws Exception { + + assertU("less common case, a complex addition with options", + add(doc("id", "4059", + "subject", "Who Me?"), + "allowDups", "true")); + + assertU("or just make the raw XML yourself", + "" + + doc("id", "4059", + "subject", "Who Me Again?") + ""); + + // or really make the xml yourself + assertU("4055" + +"Hoss the Hoss man Hostetter" + +""); + + assertU(""); + assertU(""); + + /* access the default LocalRequestFactory directly to make a request */ + SolrQueryRequest req = lrf.makeRequest( "subject:Hoss" ); + assertQ("couldn't find subject hoss", + req + ,"//result[@numFound=1]" + ,"//int[@name='id'][.='4055']" + ); + + /* make your own LocalRequestFactory to build a request */ + TestHarness.LocalRequestFactory l = h.getRequestFactory + ("standard",100,200,"version","2.1"); + assertQ("how did i find Mack Daddy? ", + l.makeRequest( "Mack Daddy" ) + ,"//result[@numFound=0]" + ); + + /* you can access the harness directly as well*/ + assertNull("how did i find Mack Daddy? ", + h.validateQuery(l.makeRequest( "Mack Daddy" ) + ,"//result[@numFound=0]" + )); + + } +} + +