Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package org.apache.solr;
  
  /*
   * 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.
  */
 
 import java.io.File;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 
 import org.junit.Test;
Helper base class for distributed search test cases

Since:
solr 1.5
 
 public abstract class BaseDistributedSearchTestCase extends SolrTestCaseJ4 {
   // TODO: this shouldn't be static. get the random when you need it to avoid sharing.
   public static Random r;
 
   private final AtomicInteger nodeCnt = new AtomicInteger(0);
   protected boolean useExplicitNodeNames;
 
   public static void initialize() {
     assumeFalse("SOLR-4147: ibm 64bit has jvm bugs!". && ..startsWith("IBM"));
      = new Random(random().nextLong());
   }

  
Set's the value of the "hostContext" system property to a random path like string (which may or may not contain sub-paths). This is used in the default constructor for this test to help ensure no code paths have hardcoded assumptions about the servlet context used to run solr.

Test configs may use the ${hostContext} variable to access this system property.

 
   public static void initHostContext() {
     // Can't use randomRealisticUnicodeString because unescaped unicode is
     // not allowed in URL paths
     // Can't use URLEncoder.encode(randomRealisticUnicodeString) because
     // Jetty freaks out and returns 404's when the context uses escapes
    StringBuilder hostContext = new StringBuilder("/");
    if (random().nextBoolean()) {
      // half the time we use the root context, the other half...
      // Remember: randomSimpleString might be the empty string
      hostContext.append(_TestUtil.randomSimpleString(random(), 2));
      if (random().nextBoolean()) {
        hostContext.append("_");
      }
      hostContext.append(_TestUtil.randomSimpleString(random(), 3));
      if ( ! "/".equals(hostContext.toString())) {
        // if our random string is empty, this might add a trailing slash,
        // but our code should be ok with that
        hostContext.append("/").append(_TestUtil.randomSimpleString(random(), 2));
      } else {
        // we got 'lucky' and still just have the root context,
        // NOOP: don't try to add a subdir to nothing (ie "//" is bad)
      }
    }
    // paranoia, we *really* don't want to ever get "//" in a path...
    final String hc = hostContext.toString().replaceAll("\\/+","/");
    .info("Setting hostContext system property: " + hc);
    System.setProperty("hostContext"hc);
  }

  
Clears the "hostContext" system property

  public static void clearHostContext() throws Exception {
    System.clearProperty("hostContext");
  }
    String ctx = System.getProperty("hostContext","/solr");
    if ("".equals(ctx)) ctx = "/solr";
    if (ctx.endsWith("/")) ctx = ctx.substring(0,ctx.length()-1);;
    if (!ctx.startsWith("/")) ctx = "/" + ctx;
    return ctx;
  }

  
Constructs a test in which the jetty+solr instances as well as the solr clients all use the value of the "hostContext" system property.

If the system property is not set, or is set to the empty string (neither of which should normally happen unless a subclass explicitly modifies the property set by initHostContext() prior to calling this constructor) a servlet context of "/solr" is used. (this is for consistency with the default behavior of solr.xml parsing when using hostContext="${hostContext:}"

If the system property is set to a value which does not begin with a "/" (which should normally happen unless a subclass explicitly modifies the property set by initHostContext() prior to calling this constructor) a leading "/" will be prepended.

  }

  

Parameters:
context explicit servlet context path to use (eg: "/solr")
  protected BaseDistributedSearchTestCase(final String context) {
    this. = context;
    this. = new String[] {"[ff01::114]:33332" + context,
                                     "[ff01::083]:33332" + context,
                                     "[ff01::213]:33332" + context};
  }
  protected int shardCount = 4;      // the actual number of solr cores that will be created in the cluster

  
Sub classes can set this flag in their constructor to true if they want to fix the number of shards to 'shardCount' The default is false which means that test will be executed with 1, 2, 3, ....shardCount number of shards repeatedly
  protected boolean fixShardCount = false;
  protected List<SolrServerclients = new ArrayList<SolrServer>();
  protected String context;
  protected String[] deadServers;
  protected String shards;
  protected String[] shardsArr;
  protected File testDir;
  protected SolrServer controlClient;
  // to stress with higher thread counts and requests, make sure the junit
  // xml formatter is not being used (all output will be buffered before
  // transformation to xml and cause an OOM exception).
  protected int stress =  ? 2 : 0;
  protected boolean verifyStress = true;
  protected int nThreads = 3;
  public static int ORDERED = 1;
  public static int SKIP = 2;
  public static int SKIPVAL = 4;
  public static int UNORDERED = 8;
  protected int flags;
  protected Map<StringIntegerhandle = new HashMap<StringInteger>();
  protected String id = "id";
  public static Logger log = LoggerFactory.getLogger(BaseDistributedSearchTestCase.class);
  public static RandVal rint = new RandVal() {
    @Override
    public Object val() {
      return .nextInt();
    }
  };
  public static RandVal rlong = new RandVal() {
    @Override
    public Object val() {
      return .nextLong();
    }
  };
  public static RandVal rfloat = new RandVal() {
    @Override
    public Object val() {
      return .nextFloat();
    }
  };
  public static RandVal rdouble = new RandVal() {
    @Override
    public Object val() {
      return .nextDouble();
    }
  };
  public static RandVal rdate = new RandDate();

  
Perform the actual tests here

Throws:
java.lang.Exception on error
  public abstract void doTest() throws Exception;
  public static String[] fieldNames = new String[]{"n_ti1""n_f1""n_tf1""n_d1""n_td1""n_l1""n_tl1""n_dt1""n_tdt1"};
  public static RandVal[] randVals = new RandVal[]{};
  protected String[] getFieldNames() {
    return ;
  }
  protected RandVal[] getRandValues() {
    return ;
  }

  
Subclasses can override this to change a test's solr home (default is in test-files)
  public String getSolrHome() {
    return SolrTestCaseJ4.TEST_HOME();
  }
  public void setUp() throws Exception {
    SolrTestCaseJ4.resetExceptionIgnores();  // ignore anything with ignore_exception in it
    super.setUp();
    System.setProperty("solr.test.sys.prop1""propone");
    System.setProperty("solr.test.sys.prop2""proptwo");
     = new File(,
            getClass().getName() + "-" + System.currentTimeMillis());
    .mkdirs();
  }
  public void tearDown() throws Exception {
    if (!AbstractSolrTestCase.recurseDelete()) {
      ..println("!!!! WARNING: best effort to remove " + .getAbsolutePath() + " FAILED !!!!!");
    }
    ..purgeAllCaches();   // avoid FC insanity
    super.tearDown();
  }
  protected JettySolrRunner createControlJetty() throws Exception {
    JettySolrRunner jetty = createJetty(new File(getSolrHome()),  + "/control/data"nullgetSolrConfigFile(), getSchemaFile());
    return jetty;
  }
  
  protected void createServers(int numShardsthrows Exception {
     = new String[numShards];
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < numShardsi++) {
      if (sb.length() > 0) sb.append(',');
      JettySolrRunner j = createJetty(new File(getSolrHome()),
           + "/shard" + i + "/data"nullgetSolrConfigFile(),
          getSchemaFile());
      .add(j);
      String shardStr = "127.0.0.1:" + j.getLocalPort() + ;
      [i] = shardStr;
      sb.append(shardStr);
    }
     = sb.toString();
  }
  protected void setDistributedParams(ModifiableSolrParams params) {
    params.set("shards"getShardsString());
  }
  protected String getShardsString() {
    if ( == nullreturn ;
    StringBuilder sb = new StringBuilder();
    for (String shard : ) {
      if (sb.length() > 0) sb.append(',');
      int nDeadServers = .nextInt(.+1);
      if (nDeadServers > 0) {
        List<Stringreplicas = new ArrayList<String>(Arrays.asList());
        Collections.shuffle(replicas);
        replicas.add(.nextInt(nDeadServers+1), shard);
        for (int i=0; i<nDeadServers+1; i++) {
          if (i!=0) sb.append('|');
          sb.append(replicas.get(i));
        }
      } else {
        sb.append(shard);
      }
    }
    return sb.toString();
  }
  protected void destroyServers() throws Exception {
    for (JettySolrRunner jetty : jetty.stop();
    for (SolrServer client : ) ((HttpSolrServerclient).shutdown();
    .clear();
    .clear();
  }
  public JettySolrRunner createJetty(File solrHomeString dataDirthrows Exception {
    return createJetty(solrHomedataDirnullnullnull);
  }
  public JettySolrRunner createJetty(File solrHomeString dataDirString shardIdthrows Exception {
    return createJetty(solrHomedataDirshardIdnullnull);
  }
  public JettySolrRunner createJetty(File solrHomeString dataDirString shardListString solrConfigOverrideString schemaOverridethrows Exception {
    return createJetty(solrHomedataDirshardListsolrConfigOverrideschemaOverride);
  }
  public JettySolrRunner createJetty(File solrHomeString dataDirString shardListString solrConfigOverrideString schemaOverrideboolean explicitCoreNodeNamethrows Exception {
    boolean stopAtShutdown = true;
    JettySolrRunner jetty = new JettySolrRunner
        (solrHome.getAbsolutePath(), , 0, solrConfigOverrideschemaOverridestopAtShutdown,
          getExtraServlets(), nullgetExtraRequestFilters());
    jetty.setShards(shardList);
    jetty.setDataDir(dataDir);
    if (explicitCoreNodeName) {
      jetty.setCoreNodeName(Integer.toString(.incrementAndGet()));
    }
    jetty.start();
    return jetty;
  }

  
Override this method to insert extra servlets into the JettySolrRunners that are created using createJetty()
    return null;
  }

  
Override this method to insert extra filters into the JettySolrRunners that are created using createJetty()
    return null;
  }
  protected SolrServer createNewSolrServer(int port) {
    try {
      // setup the server...
      String url = "http://127.0.0.1:" + port + ;
      HttpSolrServer s = new HttpSolrServer(url);
      s.setSoTimeout(60000);
      s.setMaxTotalConnections(100);
      return s;
    }
    catch (Exception ex) {
      throw new RuntimeException(ex);
    }
  }
  protected void addFields(SolrInputDocument docObject... fields) {
    for (int i = 0; i < fields.lengthi += 2) {
      doc.addField((String) (fields[i]), fields[i + 1]);
    }
  }// add random fields to the documet before indexing
  protected void indexr(Object... fieldsthrows Exception {
    SolrInputDocument doc = new SolrInputDocument();
    addFields(docfields);
    addFields(doc"rnd_b"true);
    addRandFields(doc);
    indexDoc(doc);
  }
    return sdoc;
  }
  protected void index(Object... fieldsthrows Exception {
    SolrInputDocument doc = new SolrInputDocument();
    addFields(docfields);
    indexDoc(doc);
  }

  
Indexes the document in both the control client, and a randomly selected client
  protected void indexDoc(SolrInputDocument docthrows IOExceptionSolrServerException {
    .add(doc);
    int which = (doc.getField().toString().hashCode() & 0x7fffffff) % .size();
    SolrServer client = .get(which);
    client.add(doc);
  }

  
Indexes the document in both the control client and the specified client asserting that the respones are equivilent
  protected UpdateResponse indexDoc(SolrServer serverSolrParams paramsSolrInputDocument... sdocsthrows IOExceptionSolrServerException {
    UpdateResponse controlRsp = add(paramssdocs);
    UpdateResponse specificRsp = add(serverparamssdocs);
    compareSolrResponses(specificRspcontrolRsp);
    return specificRsp;
  }
  protected UpdateResponse add(SolrServer serverSolrParams paramsSolrInputDocument... sdocsthrows IOExceptionSolrServerException {
    UpdateRequest ureq = new UpdateRequest();
    ureq.setParams(new ModifiableSolrParams(params));
    for (SolrInputDocument sdoc : sdocs) {
      ureq.add(sdoc);
    }
    return ureq.process(server);
  }
  protected UpdateResponse del(SolrServer serverSolrParams paramsObject... idsthrows IOExceptionSolrServerException {
    UpdateRequest ureq = new UpdateRequest();
    ureq.setParams(new ModifiableSolrParams(params));
    for (Object idids) {
      ureq.deleteById(id.toString());
    }
    return ureq.process(server);
  }
  protected UpdateResponse delQ(SolrServer serverSolrParams paramsString... queriesthrows IOExceptionSolrServerException {
    UpdateRequest ureq = new UpdateRequest();
    ureq.setParams(new ModifiableSolrParams(params));
    for (String qqueries) {
      ureq.deleteByQuery(q);
    }
    return ureq.process(server);
  }
  protected void index_specific(int serverNumberObject... fieldsthrows Exception {
    SolrInputDocument doc = new SolrInputDocument();
    for (int i = 0; i < fields.lengthi += 2) {
      doc.addField((String) (fields[i]), fields[i + 1]);
    }
    .add(doc);
    SolrServer client = .get(serverNumber);
    client.add(doc);
  }
  protected void del(String qthrows Exception {
    for (SolrServer client : ) {
      client.deleteByQuery(q);
    }
  }// serial commit...
  protected void commit() throws Exception {
    for (SolrServer client : ) {
      client.commit();
    }
  }
    // query a random server
    int which = .nextInt(.size());
    SolrServer client = .get(which);
    QueryResponse rsp = client.query(params);
    return rsp;
  }

  
Sets distributed params. Returns the QueryResponse from queryServer(org.apache.solr.common.params.ModifiableSolrParams),
  protected QueryResponse query(Object... qthrows Exception {
    return query(trueq);
  }

  
Sets distributed params. Returns the QueryResponse from queryServer(org.apache.solr.common.params.ModifiableSolrParams),
  protected QueryResponse query(SolrParams paramsthrows Exception {
    return query(trueparams);
  }

  
  protected QueryResponse query(boolean setDistribParamsObject[] qthrows Exception {
    final ModifiableSolrParams params = new ModifiableSolrParams();
    for (int i = 0; i < q.lengthi += 2) {
      params.add(q[i].toString(), q[i + 1].toString());
    }
    return query(setDistribParamsparams);
  }

  
  protected QueryResponse query(boolean setDistribParamsSolrParams pthrows Exception {
    final ModifiableSolrParams params = new ModifiableSolrParams(p);
    // TODO: look into why passing true causes fails
    params.set("distrib""false");
    final QueryResponse controlRsp = .query(params);
    validateControlData(controlRsp);
    params.remove("distrib");
    if (setDistribParamssetDistributedParams(params);
    QueryResponse rsp = queryServer(params);
    compareResponses(rspcontrolRsp);
    if ( > 0) {
      .info("starting stress...");
      Thread[] threads = new Thread[];
      for (int i = 0; i < threads.lengthi++) {
        threads[i] = new Thread() {
          @Override
          public void run() {
            for (int j = 0; j < j++) {
              int which = .nextInt(.size());
              SolrServer client = .get(which);
              try {
                QueryResponse rsp = client.query(new ModifiableSolrParams(params));
                if () {
                  compareResponses(rspcontrolRsp);
                }
              } catch (SolrServerException e) {
                throw new RuntimeException(e);
              }
            }
          }
        };
        threads[i].start();
      }
      for (Thread thread : threads) {
        thread.join();
      }
    }
    return rsp;
  }
  public QueryResponse queryAndCompare(SolrParams paramsSolrServer... serversthrows SolrServerException {
    return queryAndCompare(params, Arrays.<SolrServer>asList(servers));
  }
    QueryResponse first = null;
    for (SolrServer server : servers) {
      QueryResponse rsp = server.query(new ModifiableSolrParams(params));
      if (first == null) {
        first = rsp;
      } else {
        compareResponses(firstrsp);
      }
    }
    return first;
  }
  public static boolean eq(String aString b) {
    return a == b || (a != null && a.equals(b));
  }
  public static int flags(Map<StringIntegerhandleObject key) {
    if (handle == nullreturn 0;
    Integer f = handle.get(key);
    return f == null ? 0 : f;
  }
  public static String compare(NamedList aNamedList bint flagsMap<StringIntegerhandle) {
//    System.out.println("resp a:" + a);
//    System.out.println("resp b:" + b);
    boolean ordered = (flags & ) == 0;
    if (!ordered) {
      Map mapA = new HashMap(a.size());
      for (int i=0; i<a.size(); i++) {
        Object prev = mapA.put(a.getName(i), a.getVal(i));
      }
      Map mapB = new HashMap(b.size());
      for (int i=0; i<b.size(); i++) {
        Object prev = mapB.put(b.getName(i), b.getVal(i));
      }
      return compare(mapAmapBflagshandle);
    }
    int posa = 0, posb = 0;
    int aSkipped = 0, bSkipped = 0;
    for (; ;) {
      if (posa >= a.size() && posb >= b.size()) {
        break;
      }
      String namea = nullnameb = null;
      Object vala = nullvalb = null;
      int flagsa = 0, flagsb = 0;
      while (posa < a.size()) {
        namea = a.getName(posa);
        vala = a.getVal(posa);
        posa++;
        flagsa = flags(handlenamea);
        if ((flagsa & ) != 0) {
          namea = nullvala = null;
          aSkipped++;
          continue;
        }
        break;
      }
      while (posb < b.size()) {
        nameb = b.getName(posb);
        valb = b.getVal(posb);
        posb++;
        flagsb = flags(handlenameb);
        if ((flagsb & ) != 0) {
          nameb = nullvalb = null;
          bSkipped++;
          continue;
        }
        if (eq(nameanameb)) {
          break;
        }
        return "." + namea + "!=" + nameb + " (unordered or missing)";
        // if unordered, continue until we find the right field.
      }
      // ok, namea and nameb should be equal here already.
      if ((flagsa & ) != 0) continue;  // keys matching is enough
      String cmp = compare(valavalbflagsahandle);
      if (cmp != nullreturn "." + namea + cmp;
    }
    if (a.size() - aSkipped != b.size() - bSkipped) {
      return ".size()==" + a.size() + "," + b.size() + " skipped=" + aSkipped + "," + bSkipped;
    }
    return null;
  }
  public static String compare1(Map aMap bint flagsMap<StringIntegerhandle) {
    String cmp;
    for (Object keya : a.keySet()) {
      Object vala = a.get(keya);
      int flagsa = flags(handlekeya);
      if ((flagsa & ) != 0) continue;
      if (!b.containsKey(keya)) {
        return "[" + keya + "]==null";
      }
      if ((flagsa & ) != 0) continue;
      Object valb = b.get(keya);
      cmp = compare(valavalbflagsahandle);
      if (cmp != nullreturn "[" + keya + "]" + cmp;
    }
    return null;
  }
  public static String compare(Map aMap bint flagsMap<StringIntegerhandle) {
    String cmp;
    cmp = compare1(abflagshandle);
    if (cmp != nullreturn cmp;
    return compare1(baflagshandle);
  }
  public static String compare(SolrDocument aSolrDocument bint flagsMap<StringIntegerhandle) {
    return compare(a.getFieldValuesMap(), b.getFieldValuesMap(), flagshandle);
  }
  public static String compare(SolrDocumentList aSolrDocumentList bint flagsMap<StringIntegerhandle) {
    boolean ordered = (flags & ) == 0;
    String cmp;
    int f = flags(handle"maxScore");
    if ((f & ) == 0) {
      cmp = compare(a.getMaxScore(), b.getMaxScore(), 0, handle);
      if (cmp != nullreturn ".maxScore" + cmp;
    } else {
      if (b.getMaxScore() != null) {
        if (a.getMaxScore() == null) {
          return ".maxScore missing";
        }
      }
    }
    cmp = compare(a.getNumFound(), b.getNumFound(), 0, handle);
    if (cmp != nullreturn ".numFound" + cmp;
    cmp = compare(a.getStart(), b.getStart(), 0, handle);
    if (cmp != nullreturn ".start" + cmp;
    cmp = compare(a.size(), b.size(), 0, handle);
    if (cmp != nullreturn ".size()" + cmp;
    // only for completely ordered results (ties might be in a different order)
    if (ordered) {
      for (int i = 0; i < a.size(); i++) {
        cmp = compare(a.get(i), b.get(i), 0, handle);
        if (cmp != nullreturn "[" + i + "]" + cmp;
      }
      return null;
    }
    // unordered case
    for (int i = 0; i < a.size(); i++) {
      SolrDocument doc = a.get(i);
      Object key = doc.getFirstValue("id");
      SolrDocument docb = null;
      if (key == null) {
        // no id field to correlate... must compare ordered
        docb = b.get(i);
      } else {
        for (int j = 0; j < b.size(); j++) {
          docb = b.get(j);
          if (key.equals(docb.getFirstValue("id"))) break;
        }
      }
      // if (docb == null) return "[id="+key+"]";
      cmp = compare(docdocb, 0, handle);
      if (cmp != nullreturn "[id=" + key + "]" + cmp;
    }
    return null;
  }
  public static String compare(Object[] aObject[] bint flagsMap<StringIntegerhandle) {
    if (a.length != b.length) {
      return ".length:" + a.length + "!=" + b.length;
    }
    for (int i = 0; i < a.lengthi++) {
      String cmp = compare(a[i], b[i], flagshandle);
      if (cmp != nullreturn "[" + i + "]" + cmp;
    }
    return null;
  }
  public static String compare(Object aObject bint flagsMap<StringIntegerhandle) {
    if (a == breturn null;
    if (a == null || b == nullreturn ":" + a + "!=" + b;
    if (a instanceof NamedList && b instanceof NamedList) {
      return compare((NamedLista, (NamedListbflagshandle);
    }
    if (a instanceof SolrDocumentList && b instanceof SolrDocumentList) {
      return compare((SolrDocumentLista, (SolrDocumentListbflagshandle);
    }
    if (a instanceof SolrDocument && b instanceof SolrDocument) {
      return compare((SolrDocumenta, (SolrDocumentbflagshandle);
    }
    if (a instanceof Map && b instanceof Map) {
      return compare((Mapa, (Mapbflagshandle);
    }
    if (a instanceof Object[] && b instanceof Object[]) {
      return compare((Object[]) a, (Object[]) bflagshandle);
    }
    if (a instanceof byte[] && b instanceof byte[]) {
      if (!Arrays.equals((byte[]) a, (byte[]) b)) {
        return ":" + a + "!=" + b;
      }
      return null;
    }
    if (a instanceof List && b instanceof List) {
      return compare(((Lista).toArray(), ((Listb).toArray(), flagshandle);
    }
    if (!(a.equals(b))) {
      return ":" + a + "!=" + b;
    }
    return null;
  }
  protected void compareSolrResponses(SolrResponse aSolrResponse b) {
    String cmp = compare(a.getResponse(), b.getResponse(), );
    if (cmp != null) {
      .error("Mismatched responses:\n" + a + "\n" + b);
      Assert.fail(cmp);
    }
  }
  protected void compareResponses(QueryResponse aQueryResponse b) {
    if (System.getProperty("remove.version.field") != null) {
      // we don't care if one has a version and the other doesnt -
      // control vs distrib
      // TODO: this should prob be done by adding an ignore on _version_ rather than mutating the responses?
      if (a.getResults() != null) {
        for (SolrDocument doc : a.getResults()) {
          doc.removeFields("_version_");
        }
      }
      if (b.getResults() != null) {
        for (SolrDocument doc : b.getResults()) {
          doc.removeFields("_version_");
        }
      }
    }
    compareSolrResponses(ab);
  }
  @Test
  public void testDistribSearch() throws Exception {
    if () {
      . = new HashSet(); //reset random values
      doTest();
      destroyServers();
    } else {
      for (int nServers = 1; nServers < nServers++) {
        createServers(nServers);
        . = new HashSet(); //reset random values
        doTest();
        destroyServers();
      }
    }
  }
  public static Object[] getRandFields(String[] fieldsRandVal[] randVals) {
    Object[] o = new Object[fields.length * 2];
    for (int i = 0; i < fields.lengthi++) {
      o[i * 2] = fields[i];
      o[i * 2 + 1] = randVals[i].uval();
    }
    return o;
  }

  
Implementations can pre-test the control data for basic correctness before using it as a check for the shard data. This is useful, for instance, if a test bug is introduced causing a spelling index not to get built: both control & shard data would have no results but because they match the test would pass. This method gives us a chance to ensure something exists in the control data.
  public void validateControlData(QueryResponse controlthrows Exception {
    /* no-op */
  }
  public static abstract class RandVal {
    public static Set uniqueValues = new HashSet();
    public abstract Object val();
    public Object uval() {
      for (; ;) {
        Object v = val();
        if (.add(v)) return v;
      }
    }
  }
  public static class RandDate extends RandVal {
    public static TrieDateField df = new TrieDateField();
    @Override
    public Object val() {
      long v = .nextLong();
      Date d = new Date(v);
      return .toExternal(d);
    }
  }
  protected String getSolrXml() {
    return null;
  }
  protected void setupJettySolrHome(File jettyHomethrows IOException {
    FileUtils.copyDirectory(new File(getSolrHome()), jettyHome);
    String solrxml = getSolrXml();
    if (solrxml != null) {
      FileUtils.copyFile(new File(getSolrHome(), solrxml), new File(jettyHome"solr.xml"));
    }
  }