diff --git a/solr/build.xml b/solr/build.xml index 4014df8c1d8..87abc247a7f 100644 --- a/solr/build.xml +++ b/solr/build.xml @@ -341,6 +341,7 @@ + diff --git a/solr/src/test/org/apache/solr/SolrTestCaseJ4.java b/solr/src/test/org/apache/solr/SolrTestCaseJ4.java new file mode 100755 index 00000000000..2c0f8857e43 --- /dev/null +++ b/solr/src/test/org/apache/solr/SolrTestCaseJ4.java @@ -0,0 +1,431 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.lucene.util.LuceneTestCaseJ4; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.SolrInputField; +import org.apache.solr.common.util.XML; +import org.apache.solr.core.SolrConfig; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.util.TestHarness; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; + +import javax.xml.xpath.XPathExpressionException; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * A junit4 Solr test harness that extends LuceneTestCaseJ4. + * Unlike AbstractSolrTestCase, a new core is not created for each test method. + * + */ +public class SolrTestCaseJ4 extends LuceneTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + } + + @AfterClass + public static void afterClass() throws Exception { + deleteCore(); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + log.info("###Starting " + getName()); // returns ??? + } + + @Override + public void tearDown() throws Exception { + log.info("###Ending " + getName()); + super.tearDown(); + } + + /** Call initCore in @BeforeClass to instantiate a solr core in your test class, + * then call deleteCore in @AfterClass */ + public static void initCore(String config, String schema) throws Exception { + configString = config; + schemaString = schema; + initCore(); + } + + + protected static String configString; + protected static String schemaString; + + protected static SolrConfig solrConfig; + /** + * Harness initialized by initTestHarness. + * + *

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

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

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

+ */ + protected static TestHarness.LocalRequestFactory lrf; + + + /** + * Subclasses must define this method to return the name of the + * schema.xml they wish to use. + */ + public static String getSchemaFile() { + return schemaString; + }; + + /** + * Subclasses must define this method to return the name of the + * solrconfig.xml they wish to use. + */ + public static String getSolrConfigFile() { + return configString; + }; + + /** + * The directory used to story the index managed by the TestHarness h + */ + protected static File dataDir; + + /** + * Initializes things your test might need + * + * + * + */ + + public static Logger log = LoggerFactory.getLogger(SolrTestCaseJ4.class); + + public static void initCore() throws Exception { + log.info("####initCore"); + dataDir = new File(System.getProperty("java.io.tmpdir") + + System.getProperty("file.separator") + // + getClass().getName() + "-" + System.currentTimeMillis()); + + System.currentTimeMillis()); + dataDir.mkdirs(); + + String configFile = getSolrConfigFile(); + if (configFile != null) { + + solrConfig = h.createConfig(getSolrConfigFile()); + h = new TestHarness( dataDir.getAbsolutePath(), + solrConfig, + getSchemaFile()); + lrf = h.getRequestFactory + ("standard",0,20,"version","2.2"); + } + log.info("####initCore end"); + } + + /** Subclasses that override setUp can optionally call this method + * to log the fact that their setUp process has ended. + */ + public void postSetUp() { + log.info("####POSTSETUP " + getName()); + } + + + /** Subclasses that override tearDown can optionally call this method + * to log the fact that the tearDown process has started. This is necessary + * since subclasses will want to call super.tearDown() at the *end* of their + * tearDown method. + */ + public void preTearDown() { + log.info("####PRETEARDOWN " + getName()); + } + + /** + * Shuts down the test harness, and makes the best attempt possible + * to delete dataDir, unless the system property "solr.test.leavedatadir" + * is set. + */ + public static void deleteCore() throws Exception { + log.info("###deleteCore" ); + if (h != null) { h.close(); } + if (dataDir != null) { + 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 !!!!!"); + } + } + } + + dataDir = null; + solrConfig = null; + h = null; + lrf = null; + configString = schemaString = null; + } + + + /** 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) { + checkUpdateU(message, update, true); + } + + /** Validates an update XML String failed + */ + public void assertFailedU(String update) { + assertFailedU(null, update); + } + + /** Validates an update XML String failed + */ + public void assertFailedU(String message, String update) { + checkUpdateU(message, update, false); + } + + /** Checks the success or failure of an update message + */ + private void checkUpdateU(String message, String update, boolean shouldSucceed) { + try { + String m = (null == message) ? "" : message + " "; + if (shouldSucceed) { + String res = h.validateUpdate(update); + if (res != null) fail(m + "update was not successful: " + res); + } else { + String res = h.validateErrorUpdate(update); + if (res != null) fail(m + "update succeeded, but should have failed: " + 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 + + "\n xml response was: " + response + + "\n request was: " + req.getParamString()); + } + } catch (XPathExpressionException e1) { + throw new RuntimeException("XPath is invalid", e1); + } catch (Exception e2) { + throw new RuntimeException("Exception during query", e2); + } + } + + /** Makes sure a query throws a SolrException with the listed response code */ + public static void assertQEx(String message, SolrQueryRequest req, int code ) { + try { + h.query(req); + fail( message ); + } catch (SolrException sex) { + assertEquals( code, sex.code() ); + } catch (Exception e2) { + throw new RuntimeException("Exception during query", e2); + } + } + + public static void assertQEx(String message, SolrQueryRequest req, SolrException.ErrorCode code ) { + try { + h.query(req); + fail( message ); + } catch (SolrException e) { + assertEquals( code.code, e.code() ); + } catch (Exception e2) { + throw new RuntimeException("Exception during query", e2); + } + } + + + /** + * @see TestHarness#optimize + */ + public static String optimize(String... args) { + return h.optimize(args); + } + /** + * @see TestHarness#commit + */ + public static String commit(String... args) { + return h.commit(args); + } + + /** + * 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 static String adoc(String... fieldsAndValues) { + Doc d = doc(fieldsAndValues); + return add(d); + } + + /** + * Generates a simple <add><doc>... XML String with no options + */ + public static String adoc(SolrInputDocument sdoc) { + List fields = new ArrayList(); + for (SolrInputField sf : sdoc) { + for (Object o : sf.getValues()) { + fields.add(sf.getName()); + fields.add(o.toString()); + } + } + return adoc(fields.toArray(new String[fields.size()])); + } + + + /** + * 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 static String add(Doc doc, String... args) { + try { + StringWriter r = new StringWriter(); + + // this is anoying + if (null == args || 0 == args.length) { + r.write(""); + r.write(doc.xml); + r.write(""); + } else { + XML.writeUnescapedXML(r, "add", doc.xml, (Object[])args); + } + + return r.getBuffer().toString(); + } catch (IOException e) { + throw new RuntimeException + ("this should never happen with a StringWriter", e); + } + } + + /** + * Generates a <delete>... XML string for an ID + * + * @see TestHarness#deleteById + */ + public static String delI(String id) { + return h.deleteById(id); + } + /** + * Generates a <delete>... XML string for an query + * + * @see TestHarness#deleteByQuery + */ + public static 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 static 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 static SolrQueryRequest req(String... q) { + return lrf.makeRequest(q); + } + + /** + * Generates a SolrQueryRequest using the LocalRequestFactory + * @see #lrf + */ + public static SolrQueryRequest req(String[] params, String... moreParams) { + String[] allParams = moreParams; + if (params.length!=0) { + int len = params.length + moreParams.length; + allParams = new String[len]; + System.arraycopy(params,0,allParams,0,params.length); + System.arraycopy(moreParams,0,allParams,params.length,moreParams.length); + } + + return lrf.makeRequest(allParams); + } + + /** Neccessary to make method signatures un-ambiguous */ + public static class Doc { + public String xml; + public String toString() { return xml; } + } + + public static boolean recurseDelete(File f) { + if (f.isDirectory()) { + for (File sub : f.listFiles()) { + if (!recurseDelete(sub)) { + return false; + } + } + } + return f.delete(); + } +} diff --git a/solr/src/test/org/apache/solr/request/SimpleFacetsTest.java b/solr/src/test/org/apache/solr/request/SimpleFacetsTest.java index edf343d4b21..62280c84e41 100644 --- a/solr/src/test/org/apache/solr/request/SimpleFacetsTest.java +++ b/solr/src/test/org/apache/solr/request/SimpleFacetsTest.java @@ -17,14 +17,20 @@ package org.apache.solr.request; +import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.util.AbstractSolrTestCase; +import org.junit.BeforeClass; +import org.junit.Test; -public class SimpleFacetsTest extends AbstractSolrTestCase { - public String getSchemaFile() { return "schema.xml"; } - public String getSolrConfigFile() { return "solrconfig.xml"; } - public String getCoreName() { return "basic"; } - +public class SimpleFacetsTest extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml","schema.xml"); + } + + @Test public void testSimpleFacetCounts() { assertU(adoc("id", "42", "trait_s", "Tool", "trait_s", "Obnoxious", "name", "Zapp Brannigan")); @@ -210,7 +216,8 @@ public class SimpleFacetsTest extends AbstractSolrTestCase { ); } - + + @Test public void testDateFacets() { final String f = "bday"; final String pre = "//lst[@name='facet_dates']/lst[@name='"+f+"']"; @@ -382,6 +389,7 @@ public class SimpleFacetsTest extends AbstractSolrTestCase { } + @Test public void testFacetMultiValued() { doFacetPrefix("t_s", "facet.method","enum"); doFacetPrefix("t_s", "facet.method", "enum", "facet.enum.cache.minDf", "2"); @@ -389,6 +397,7 @@ public class SimpleFacetsTest extends AbstractSolrTestCase { doFacetPrefix("t_s", "facet.method", "fc"); } + @Test public void testFacetSingleValued() { doFacets("t_s1"); } @@ -557,7 +566,7 @@ public class SimpleFacetsTest extends AbstractSolrTestCase { } - + @Test public void testFacetPrefixMultiValued() { doFacetPrefix("t_s", "facet.method","enum"); doFacetPrefix("t_s", "facet.method", "enum", "facet.enum.cache.minDf", "3"); @@ -565,6 +574,7 @@ public class SimpleFacetsTest extends AbstractSolrTestCase { doFacetPrefix("t_s", "facet.method", "fc"); } + @Test public void testFacetPrefixSingleValued() { doFacetPrefix("t_s1"); }