mirror of https://github.com/apache/lucene.git
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
This commit is contained in:
parent
4e0b46dd4e
commit
b2aca12c43
58
build.xml
58
build.xml
|
@ -29,6 +29,11 @@
|
||||||
<property name="javadoc.packages" value="org.apache.solr.*"/>
|
<property name="javadoc.packages" value="org.apache.solr.*"/>
|
||||||
<property name="build.javadoc" value="${build.docs}/api"/>
|
<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. -->
|
<!-- Default target: usage. Prints out instructions. -->
|
||||||
<target name="usage"
|
<target name="usage"
|
||||||
description="Prints out instructions">
|
description="Prints out instructions">
|
||||||
|
@ -67,11 +72,11 @@
|
||||||
|
|
||||||
<javac destdir="${dest}"
|
<javac destdir="${dest}"
|
||||||
target="1.5"
|
target="1.5"
|
||||||
source="1.5"
|
source="1.5"
|
||||||
debug="on"
|
debug="on"
|
||||||
classpathref="compile.classpath">
|
classpathref="compile.classpath">
|
||||||
<src path="${src}/java" />
|
<src path="${src}/java" />
|
||||||
<src path="${src}/webapp" />
|
<src path="${src}/webapp/src" />
|
||||||
|
|
||||||
</javac>
|
</javac>
|
||||||
</target>
|
</target>
|
||||||
|
@ -132,9 +137,11 @@
|
||||||
<!-- Run unit tests. -->
|
<!-- Run unit tests. -->
|
||||||
<target name="test"
|
<target name="test"
|
||||||
description="Runs the unit tests."
|
description="Runs the unit tests."
|
||||||
depends="compileTests">
|
depends="compileTests, junit" />
|
||||||
<echo message="TO-DO later or after we convert tests to 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">
|
<java classname="SolrTest" fork="true" dir="src/apps/SolrTest" failonerror="true">
|
||||||
<arg line="-test newtest.txt"/>
|
<arg line="-test newtest.txt"/>
|
||||||
<classpath>
|
<classpath>
|
||||||
|
@ -143,7 +150,48 @@
|
||||||
</java>
|
</java>
|
||||||
|
|
||||||
</target>
|
</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>
|
||||||
|
|
||||||
|
|
||||||
<!-- ========================================================================= -->
|
<!-- ========================================================================= -->
|
||||||
|
|
|
@ -25,6 +25,10 @@ import org.apache.solr.core.SolrCore;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public interface SolrQueryRequest {
|
public interface SolrQueryRequest {
|
||||||
|
|
||||||
|
/** All uses of this request are finished, resources can be freed */
|
||||||
|
public void close();
|
||||||
|
|
||||||
public String getParam(String name);
|
public String getParam(String name);
|
||||||
|
|
||||||
public String getQueryString();
|
public String getQueryString();
|
||||||
|
|
|
@ -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 <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("<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 <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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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 <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("<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 <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<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<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 {
|
public final static void writeXML(Writer out, String tag, String val, Object... attrs) throws IOException {
|
||||||
out.write('<');
|
out.write('<');
|
||||||
out.write(tag);
|
out.write(tag);
|
||||||
|
|
|
@ -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']"
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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",
|
||||||
|
"<add allowDups=\"true\">" +
|
||||||
|
doc("id", "4059",
|
||||||
|
"subject", "Who Me Again?") + "</add>");
|
||||||
|
|
||||||
|
// or really make the xml yourself
|
||||||
|
assertU("<add><doc><field name=\"id\">4055</field>"
|
||||||
|
+"<field name=\"subject\">Hoss the Hoss man Hostetter</field>"
|
||||||
|
+"</doc></add>");
|
||||||
|
|
||||||
|
assertU("<commit/>");
|
||||||
|
assertU("<optimize/>");
|
||||||
|
|
||||||
|
/* 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]"
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue