You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by ho...@apache.org on 2006/04/11 06:36:49 UTC

svn commit: r393142 [1/2] - in /incubator/solr/trunk: ./ src/java/org/apache/solr/request/ src/java/org/apache/solr/util/ src/test/org/apache/solr/

Author: hossman
Date: Mon Apr 10 21:36:46 2006
New Revision: 393142

URL: http://svn.apache.org/viewcvs?rev=393142&view=rev
Log:
SOLR-3 - a general purpose test harness, JUnit base class, and migrated version of SolrTest's newtest.txt to JUnit

Added:
    incubator/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java
    incubator/solr/trunk/src/java/org/apache/solr/util/TestHarness.java
    incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
    incubator/solr/trunk/src/test/org/apache/solr/ConvertedLegacyTest.java
    incubator/solr/trunk/src/test/org/apache/solr/SampleTest.java
Modified:
    incubator/solr/trunk/build.xml
    incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java
    incubator/solr/trunk/src/java/org/apache/solr/util/XML.java

Modified: incubator/solr/trunk/build.xml
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/build.xml?rev=393142&r1=393141&r2=393142&view=diff
==============================================================================
--- incubator/solr/trunk/build.xml (original)
+++ incubator/solr/trunk/build.xml Mon Apr 10 21:36:46 2006
@@ -29,6 +29,11 @@
   <property name="javadoc.packages" value="org.apache.solr.*"/>
   <property name="build.javadoc" value="${build.docs}/api"/>
 
+  <property name="junit.output.dir" location="${dest}/test-results"/>
+  <property name="junit.reports" location="${dest}/test-results/reports"/>
+  <property name="junit.includes" value="**/Test*.java,**/*Test.java"/>
+
+  
   <!-- Default target: usage.  Prints out instructions. -->
   <target name="usage"
           description="Prints out instructions">
@@ -67,11 +72,11 @@
 
     <javac destdir="${dest}"
            target="1.5"
-	   source="1.5"
+           source="1.5"
 	   debug="on"
            classpathref="compile.classpath">
       <src path="${src}/java" />
-      <src path="${src}/webapp" />
+      <src path="${src}/webapp/src" />
 
     </javac>
   </target>
@@ -132,9 +137,11 @@
   <!-- Run unit tests. -->
   <target name="test"
           description="Runs the unit tests."
-          depends="compileTests">
-    <echo message="TO-DO later or after we convert tests to JUnit." />
-
+          depends="compileTests, junit" />
+	   
+  <target name="legacyTest"
+          depends="compileTests" >
+    <!-- DEPRECATED: no description so it doesn't show up in project help -->
     <java classname="SolrTest" fork="true" dir="src/apps/SolrTest" failonerror="true">
      <arg line="-test newtest.txt"/>
      <classpath>
@@ -143,7 +150,48 @@
     </java>
 
   </target>
+  
+  <target name="junit" depends="compileTests">
+    <!-- no description so it doesn't show up in -projecthelp -->  
+    <mkdir dir="${junit.output.dir}"/>
+
+    <!-- :TODO: either SolrCore needs a way to specify the
+         solrconfig.xml, or all test are going to need to use the same
+         conf file, either way we need a specific run directory for
+         the tests.
+      -->    
+    <junit printsummary="on"
+           haltonfailure="no"
+           errorProperty="tests.failed"
+           failureProperty="tests.failed"
+           dir="src/apps/SolrTest"
+           >
+      <syspropertyset>
+        <propertyref prefix="solr" />
+      </syspropertyset>
+      <classpath refid="test.run.classpath"/>
+      <formatter type="xml"/>
+      <batchtest fork="yes" todir="${junit.output.dir}" unless="testcase">
+        <fileset dir="src/test" includes="${junit.includes}"/>
+      </batchtest>
+      <batchtest fork="yes" todir="${junit.output.dir}" if="testcase">
+        <fileset dir="src/test" includes="**/${testcase}.java"/>
+      </batchtest>
+    </junit>
 
+    <fail if="tests.failed">Tests failed!</fail>
+  </target>
+  
+  <target name="test-reports">
+    <!-- no description so it doesn't show up in -projecthelp ... yet -->  
+    <mkdir dir="${junit.reports}"/>
+    <junitreport todir="${junit.output.dir}">
+      <fileset dir="${junit.output.dir}">
+        <include name="TEST-*.xml"/>
+      </fileset>
+      <report format="frames" todir="${junit.reports}"/>
+    </junitreport>
+  </target>
 
 
   <!-- ========================================================================= -->

Modified: incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java?rev=393142&r1=393141&r2=393142&view=diff
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SolrQueryRequest.java Mon Apr 10 21:36:46 2006
@@ -25,6 +25,10 @@
  * @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();

Added: incubator/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java?rev=393142&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java Mon Apr 10 21:36:46 2006
@@ -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"
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @see #setUp
+ * @see #tearDown
+ */
+public abstract class AbstractSolrTestCase extends TestCase {
+
+  /**
+   * Harness initialized by initTestHarness.
+   *
+   * <p>
+   * For use in test methods as needed.
+   * </p>
+   */
+  protected TestHarness h;
+  /**
+   * LocalRequestFactory initialized by initTestHarness using sensible
+   * defaults.
+   *
+   * <p>
+   * For use in test methods as needed.
+   * </p>
+   */
+  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
+   *
+   * <ul>
+   * <li>Creates a dataDir in the "java.io.tmpdir"</li>
+   * <li>initializes the TestHarness h using this data directory, and getSchemaPath()</li>
+   * <li>initializes the LocalRequestFactory lrf using sensible defaults.</li>
+   * </ul>
+   *
+   */
+  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 &lt;add&gt;&lt;doc&gt;... 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 &lt;add&gt;&lt;doc&gt;... 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("<add>");
+        r.write(doc.xml);
+        r.write("</add>");
+      } 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 &lt;delete&gt;... XML string for an ID
+   *
+   * @see TestHarness#deleteById
+   */
+  public String delI(String id) {
+    return h.deleteById(id);
+  }
+  /**
+   * Generates a &lt;delete&gt;... XML string for an query
+   *
+   * @see TestHarness#deleteByQuery
+   */
+  public String delQ(String q) {
+    return h.deleteByQuery(q);
+  }
+  
+  /**
+   * Generates a simple &lt;doc&gt;... 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();
+  }
+
+    
+}

Added: incubator/solr/trunk/src/java/org/apache/solr/util/TestHarness.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/util/TestHarness.java?rev=393142&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/util/TestHarness.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/util/TestHarness.java Mon Apr 10 21:36:46 2006
@@ -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.
+ *
+ * <p>
+ * 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("<add>");
+    appendSimpleDoc(buf, fieldsAndValues);
+    buf.append("</add>");
+        
+    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 &lt;doc&gt; 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 &lt;doc&gt; 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("<doc>");
+      for (int i = 0; i < fieldsAndValues.length; i+=2) {
+        XML.writeXML(w, "field", fieldsAndValues[i+1], "name",
+                     fieldsAndValues[i]);
+      }
+      w.append("</doc>");
+      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("<delete>");
+      XML.writeXML(r, deltype, val);
+      r.write("</delete>");
+            
+      return r.getBuffer().toString();
+    } catch (IOException e) {
+      throw new RuntimeException
+        ("this should never happen with a StringWriter", e);
+    }
+  }
+    
+  /**
+   * Helper that returns an &lt;optimize&gt; 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 &lt;commit&gt; 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<String,String> 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<String,String> args = new HashMap<String,String>();
+    public LocalRequestFactory() {
+    }
+    public LocalSolrQueryRequest makeRequest(String q) {
+      return new LocalSolrQueryRequest(TestHarness.this.getCore(),
+                                       q, qtype, start, limit, args);
+    }
+  }
+}

Modified: incubator/solr/trunk/src/java/org/apache/solr/util/XML.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/util/XML.java?rev=393142&r1=393141&r2=393142&view=diff
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/util/XML.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/util/XML.java Mon Apr 10 21:36:46 2006
@@ -121,6 +121,29 @@
     }
   }
 
+  /** 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<attrs.length; i++) {
+      out.write(' ');
+      out.write(attrs[i++].toString());
+      out.write("=\"");
+      out.write(attrs[i].toString());
+      out.write("\"");
+    }
+    if (val == null) {
+      out.write("/>");
+    } else {
+      out.write('>');
+      out.write(val);
+      out.write("</");
+      out.write(tag);
+      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);

Added: incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java?rev=393142&view=auto
==============================================================================
--- incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java (added)
+++ incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java Mon Apr 10 21:36:46 2006
@@ -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("<add><doc><field name=\"id\">1</field></doc><doc><field name=\"id\">2</field></doc></add>");
+    assertEquals("<result status=\"0\"></result><result status=\"0\"></result>", res);
+    assertU("<commit/>");
+    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']"
+//             );
+//   }
+  
+    
+}

Added: incubator/solr/trunk/src/test/org/apache/solr/ConvertedLegacyTest.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/test/org/apache/solr/ConvertedLegacyTest.java?rev=393142&view=auto
==============================================================================
--- incubator/solr/trunk/src/test/org/apache/solr/ConvertedLegacyTest.java (added)
+++ incubator/solr/trunk/src/test/org/apache/solr/ConvertedLegacyTest.java Mon Apr 10 21:36:46 2006
@@ -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<String,String> args = new HashMap<String,String>();
+      
+    // compact the index, keep things from getting out of hand
+
+    assertU("<optimize/>");
+
+    // test query
+
+    assertQ(req("qlkciyopsbgzyvkylsjhchghjrdf")
+            ,"//result[@numFound='0']"
+            );
+
+    // test escaping of ";"
+
+    assertU("<delete><id>42</id></delete>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"val_s\">aa;bb</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>42</id></delete>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"val_s\">AAA</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"val_s\">BBB</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:42")
+            ,"//*[@numFound='1'] "
+            ,"//str[.='BBB']"
+            );
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"val_s\">CCC</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"val_s\">DDD</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:42")
+            ,"//*[@numFound='1'] "
+            ,"//str[.='DDD']"
+            );
+    assertU("<delete><id>42</id></delete>");
+
+    // test deletes
+
+    assertU("<delete><query>id:[100 TO 110]</query></delete>");
+    assertU("<add allowDups=\"false\"><doc><field name=\"id\">101</field></doc></add>");
+    assertU("<add allowDups=\"false\"><doc><field name=\"id\">101</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">105</field></doc></add>");
+    assertU("<add allowDups=\"false\"><doc><field name=\"id\">102</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">103</field></doc></add>");
+    assertU("<add allowDups=\"false\"><doc><field name=\"id\">101</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:[100 TO 110]")
+            ,"//*[@numFound='4']"
+            );
+    assertU("<delete><id>102</id></delete>");
+    assertU("<commit/>");
+    assertQ(req("id:[100 TO 110]")
+            ,"//*[@numFound='3']"
+            );
+    assertU("<delete><query>id:105</query></delete>");
+    assertU("<commit/>");
+    assertQ(req("id:[100 TO 110]")
+            ,"//*[@numFound='2']"
+            );
+    assertU("<delete><query>id:[100 TO 110]</query></delete>");
+    assertU("<commit/>");
+    assertQ(req("id:[100 TO 110]")
+            ,"//*[@numFound='0']"
+            );
+
+    // test range
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"val_s\">apple</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"val_s\">banana</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"val_s\">pear</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("val_s:[a TO z]")
+            ,"//*[@numFound='3'] "
+            ,"*[count(//doc)=3] "
+            ,"//*[@start='0']"
+            );
+    args = new HashMap<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"val_s\">apple</field><field name=\"val_s\">banana</field></doc></add>");
+    assertU("<commit/>");
+    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']"
+            );
+
+
+    // <delete><id>44</id></delete>
+
+    assertU("<add overwritePending=\"true\" overwriteCommitted=\"true\"><doc><field name=\"id\">44</field><field name=\"text\">red riding hood</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\" overwriteCommitted=\"false\" overwritePending=\"false\"><doc><field name=\"id\">44</field><field name=\"text\">red riding hood</field></doc></add>");
+    assertU("<add allowDups=\"true\" overwriteCommitted=\"false\" overwritePending=\"false\"><doc><field name=\"id\">44</field><field name=\"text\">big bad wolf</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"//@numFound[.='0']"
+            );
+
+    // untokenized string type
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"ssto\">and a 10.4 ?</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"//str[.='and a 10.4 ?']"
+            );
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"sind\">abc123</field></doc></add>");
+    assertU("<commit/>");
+    // TODO: how to search for something with spaces....
+
+    assertQ(req("sind:abc123")
+            ,"//@numFound[.='1'] "
+            ,"*[count(//@name[.='sind'])=0] "
+            ,"*[count(//@name[.='id'])=1]"
+            );
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"sindsto\">abc123</field></doc></add>");
+    assertU("<commit/>");
+    // TODO: how to search for something with spaces....
+
+    assertQ(req("sindsto:abc123")
+            ,"//str[.='abc123']"
+            );
+
+    // test output of multivalued fields
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"title\">yonik3</field><field name=\"title\" boost=\"2\">yonik4</field></doc></add>");
+    assertU("<commit></commit>");
+    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("<delete><query>title:yonik4</query></delete>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"//@numFound[.='0']"
+            );
+
+
+    // not visible until commit
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<commit/>");
+    assertU("<add><doc><field name=\"id\">44</field></doc></add>");
+    assertQ(req("id:44")
+            ,"//@numFound[.='0']"
+            );
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"//@numFound[.='1']"
+            );
+
+    // test configurable stop words
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"teststop\">world stopworda view</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("+id:44 +teststop:world")
+            ,"//@numFound[.='1']"
+            );
+    assertQ(req("teststop:stopworda")
+            ,"//@numFound[.='0']"
+            );
+
+    // test ignoreCase stop words
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"stopfilt\">world AnD view</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete fromPending=\"true\" fromCommitted=\"true\"><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"gack_i\">51778</field><field name=\"t_name\">cats</field></doc></add>");
+    assertU("<commit/>");
+    // 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("<delete><query>t_name:cat</query></delete>");
+    assertU("<commit/>");
+    assertQ(req("t_name:cat")
+            ,"//@numFound[.='0']"
+            );
+
+    // test that longest dynamic field match happens first
+
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"xaa\">mystr</field><field name=\"xaaa\">12321</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"//str[@name='xaa'][.='mystr']  "
+            ,"//int[@name='xaaa'][.='12321']"
+            );
+
+
+    // test integer ranges and sorting
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">1234567890</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">10</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">2</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">15</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">-1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">-987654321</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">2147483647</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">-2147483648</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_i\">0</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">1234567890</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">10</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">2</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">15</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">-1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">-987654321</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">9223372036854775807</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">-9223372036854775808</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_l\">0</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">1.4142135</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">Infinity</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">-Infinity</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">NaN</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">2</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">-1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">-987654321</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">-999999.99</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">-1e20</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_f\">0</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">1.4142135</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">Infinity</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">-Infinity</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">NaN</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">2</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">-1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">1e-100</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">-999999.99</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">-1e100</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"num_d\">0</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"a_i\">10</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"a_i\">1</field><field name=\"b_i\">100</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"a_i\">-1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"a_i\">15</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"a_i\">1</field><field name=\"b_i\">50</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id\">44</field><field name=\"a_i\">0</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><query>id_i:[1000 TO 1010]</query></delete>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1000</field><field name=\"a_i\">1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1001</field><field name=\"a_i\">10</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1002</field><field name=\"a_i\">1</field><field name=\"b_i\">100</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1003</field><field name=\"a_i\">-1</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1004</field><field name=\"a_i\">15</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1005</field><field name=\"a_i\">1</field><field name=\"b_i\">50</field></doc></add>");
+    assertU("<add allowDups=\"true\"><doc><field name=\"id_i\">1006</field><field name=\"a_i\">0</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><query>val_s:[* TO *]</query></delete>");
+    assertU("<add><doc><field name=\"id\">100</field><field name=\"val_s\">apple</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">101</field><field name=\"val_s\">banana</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">102</field><field name=\"val_s\">apple</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">103</field><field name=\"val_s\">pearing</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">104</field><field name=\"val_s\">pear</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">105</field><field name=\"val_s\">appalling</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">106</field><field name=\"val_s\">pearson</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">107</field><field name=\"val_s\">port</field></doc></add>");
+    assertU("<commit/>");
+
+    assertQ(req("val_s:a*")
+            ,"//*[@numFound='3']"
+            );
+    assertQ(req("val_s:p*")
+            ,"//*[@numFound='4']"
+            );
+    // val_s:* %//*[@numFound="8"]
+
+
+    assertU("<delete><query>id:[100 TO 110]</query></delete>");
+
+    // test copyField functionality
+
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"title\">How Now4 brown Cows</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"text\">foo bar</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">foo bar</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">foo-bar</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">Canon PowerShot SD500 7MP</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">Wi-Fi</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">'I.B.M' A's,B's,C's</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">Sony KDF-E50A10</field></doc></add>");
+    assertU("<commit/>");
+
+    // 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("<add><doc><field name=\"id\">42</field><field name=\"subword\">http://www.yahoo.com</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">--Q 1-- W2 E-3 Ok xY 4R 5-T *6-Y- 7-8-- 10A-B</field></doc></add>");
+    assertU("<commit/>");
+    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("<add><doc><field name=\"id\">42</field><field name=\"subword\">FooBarBaz</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"subword\">FooBar10</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"subword\">10FooBar</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"subword\">BAZ</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"subword\">10</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">42</field><field name=\"subword\">Mark, 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(Stop
 Filter.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.Sol
 rServlet.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(Threa
 dPool.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</field></doc></add>");
+
+
+    // 
+
+    assertU("<delete fromPending=\"true\" fromCommitted=\"true\"><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"fname_s\">Yonik</field><field name=\"here_b\">true</field><field name=\"iq_l\">10000000000</field><field name=\"description_t\">software engineer</field><field name=\"ego_d\">1e100</field><field name=\"pi_f\">3.1415962</field><field name=\"when_dt\">2005-03-18T01:14:34Z</field><field name=\"arr_f\">1.414213562</field><field name=\"arr_f\">.999</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            );
+    args = new HashMap<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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<String,String>();
+    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("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"shouldbestored\">hi</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"//*[@name='shouldbestored']"
+            );
+    assertQ(req("+id:44 +shouldbestored:hi")
+            ,"//*[@numFound='1']"
+            );
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"shouldbeunstored\">hi</field></doc></add>");
+    assertU("<commit/>");
+    assertQ(req("id:44")
+            ,"not(//*[@name='shouldbeunstored'])"
+            );
+    assertQ(req("+id:44 +shouldbeunstored:hi")
+            ,"//*[@numFound='1']"
+            );
+
+    assertU("<delete><id>44</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"shouldbeunindexed\">hi</field></doc></add>");
+    assertU("<commit/>");
+    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("  <delete>  <id>44</id>  </delete>");
+    assertU("  <add>  <doc>  <field name=\"id\">44</field>  <field name=\"shouldbestored\">hi</field>  </doc>  </add>");
+    assertU("  <commit />");
+
+    // test adding multiple docs per add command
+
+    // assertU("<delete><query>id:[0 TO 99]</query></delete>");
+    // assertU("<add><doc><field name=\"id\">1</field></doc><doc><field name=\"id\">2</field></doc></add>");
+    // assertU("<commit/>");
+    // assertQ(req("id:[0 TO 99]")
+    // ,"//*[@numFound='2']"
+    // );
+
+    // test synonym filter
+
+    assertU("<delete><query>id:[10 TO 100]</query></delete>");
+    assertU("<add><doc><field name=\"id\">10</field><field name=\"syn\">a</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">11</field><field name=\"syn\">b</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">12</field><field name=\"syn\">c</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">13</field><field name=\"syn\">foo</field></doc></add>");
+    assertU("<commit/>");
+    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("<delete><id>44</id></delete>");
+    assertU("<delete><id>45</id></delete>");
+    assertU("<add><doc><field name=\"id\">44</field><field name=\"textgap\">aa bb cc</field><field name=\"textgap\">dd ee ff</field></doc></add>");
+    assertU("<add><doc><field name=\"id\">45</field><field name=\"text\">aa bb cc</field><field name=\"text\">dd ee ff</field></doc></add>");
+    assertU("<commit/>");
+    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<String,String>();
+    args.put("version","2.0");
+    req = new LocalSolrQueryRequest(h.getCore(), "values",
+                                    "test", 0, 10, args);
+    assertQ(req
+            );
+
+  }
+}