You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@lucene.apache.org by mi...@apache.org on 2009/01/27 21:15:23 UTC

svn commit: r738219 [3/3] - in /lucene/java/trunk: ./ contrib/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/ contrib/benchmark/src/java/org/apache/lucene/benchmark/byTask/tasks/ contrib/miscellaneous/src/test/org/apache/lucene/index/ cont...

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java?rev=738219&r1=738218&r2=738219&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java Tue Jan 27 20:15:21 2009
@@ -18,27 +18,30 @@
  */
 
 import junit.framework.Test;
-import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import junit.textui.TestRunner;
 import org.apache.lucene.analysis.SimpleAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.index.CorruptIndexException;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.store.LockObtainFailedException;
 import org.apache.lucene.store.RAMDirectory;
 import org.apache.lucene.util.DocIdBitSet;
+import org.apache.lucene.util.LuceneTestCase;
 
 import java.io.IOException;
 import java.io.Serializable;
 import java.rmi.Naming;
 import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
 import java.util.BitSet;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Locale;
+import java.util.Random;
 import java.util.regex.Pattern;
 
 /**
@@ -51,472 +54,640 @@
  */
 
 public class TestSort
-extends TestCase
+extends LuceneTestCase
 implements Serializable {
 
-	private Searcher full;
-	private Searcher searchX;
-	private Searcher searchY;
-	private Query queryX;
-	private Query queryY;
-	private Query queryA;
-	private Query queryE;
-	private Query queryF;
-	private Query queryG;
-	private Sort sort;
-
-
-	public TestSort (String name) {
-		super (name);
-	}
-
-	public static void main (String[] argv) {
-		if (argv == null || argv.length < 1)
-			TestRunner.run (suite());
-		else if ("server".equals (argv[0])) {
-			TestSort test = new TestSort (null);
-			try {
-				test.startServer();
-				Thread.sleep (500000);
-			} catch (Exception e) {
-				System.out.println (e);
-				e.printStackTrace();
-			}
-		}
-	}
-
-	public static Test suite() {
-		return new TestSuite (TestSort.class);
-	}
-
-
-	// document data:
-	// the tracer field is used to determine which document was hit
-	// the contents field is used to search and sort by relevance
-	// the int field to sort by int
-	// the float field to sort by float
-	// the string field to sort by string
+  private static final int NUM_STRINGS = 6000;
+  private Searcher full;
+  private Searcher searchX;
+  private Searcher searchY;
+  private Query queryX;
+  private Query queryY;
+  private Query queryA;
+  private Query queryE;
+  private Query queryF;
+  private Query queryG;
+  private Sort sort;
+
+
+  public TestSort (String name) {
+    super (name);
+  }
+
+  public static void main (String[] argv) {
+    if (argv == null || argv.length < 1)
+      TestRunner.run (suite());
+    else if ("server".equals (argv[0])) {
+      TestSort test = new TestSort (null);
+      try {
+        test.startServer();
+        Thread.sleep (500000);
+      } catch (Exception e) {
+        System.out.println (e);
+        e.printStackTrace();
+      }
+    }
+  }
+
+  public static Test suite() {
+    return new TestSuite (TestSort.class);
+  }
+
+
+  // document data:
+  // the tracer field is used to determine which document was hit
+  // the contents field is used to search and sort by relevance
+  // the int field to sort by int
+  // the float field to sort by float
+  // the string field to sort by string
     // the i18n field includes accented characters for testing locale-specific sorting
-	private String[][] data = new String[][] {
-	// tracer  contents         int            float           string   custom   i18n               long            double, 'short', byte, 'custom parser encoding'
-	{   "A",   "x a",           "5",           "4f",           "c",     "A-3",   "p\u00EAche",      "10",           "-4.0", "3", "126", "J"},//A, x
-	{   "B",   "y a",           "5",           "3.4028235E38", "i",     "B-10",  "HAT",             "1000000000", "40.0", "24", "1", "I"},//B, y
-	{   "C",   "x a b c",       "2147483647",  "1.0",          "j",     "A-2",   "p\u00E9ch\u00E9", "99999999",   "40.00002343", "125", "15", "H"},//C, x
-	{   "D",   "y a b c",       "-1",          "0.0f",         "a",     "C-0",   "HUT",             String.valueOf(Long.MAX_VALUE),           String.valueOf(Double.MIN_VALUE), String.valueOf(Short.MIN_VALUE), String.valueOf(Byte.MIN_VALUE), "G"},//D, y
-	{   "E",   "x a b c d",     "5",           "2f",           "h",     "B-8",   "peach",           String.valueOf(Long.MIN_VALUE),           String.valueOf(Double.MAX_VALUE), String.valueOf(Short.MAX_VALUE),           String.valueOf(Byte.MAX_VALUE), "F"},//E,x
-	{   "F",   "y a b c d",     "2",           "3.14159f",     "g",     "B-1",   "H\u00C5T",        "-44",           "343.034435444", "-3", "0", "E"},//F,y
-	{   "G",   "x a b c d",     "3",           "-1.0",         "f",     "C-100", "sin",             "323254543543", "4.043544", "5", "100", "D"},//G,x
+  private String[][] data = new String[][] {
+  // tracer  contents         int            float           string   custom   i18n               long            double, 'short', byte, 'custom parser encoding'
+  {   "A",   "x a",           "5",           "4f",           "c",     "A-3",   "p\u00EAche",      "10",           "-4.0", "3", "126", "J"},//A, x
+  {   "B",   "y a",           "5",           "3.4028235E38", "i",     "B-10",  "HAT",             "1000000000", "40.0", "24", "1", "I"},//B, y
+  {   "C",   "x a b c",       "2147483647",  "1.0",          "j",     "A-2",   "p\u00E9ch\u00E9", "99999999",   "40.00002343", "125", "15", "H"},//C, x
+  {   "D",   "y a b c",       "-1",          "0.0f",         "a",     "C-0",   "HUT",             String.valueOf(Long.MAX_VALUE),           String.valueOf(Double.MIN_VALUE), String.valueOf(Short.MIN_VALUE), String.valueOf(Byte.MIN_VALUE), "G"},//D, y
+  {   "E",   "x a b c d",     "5",           "2f",           "h",     "B-8",   "peach",           String.valueOf(Long.MIN_VALUE),           String.valueOf(Double.MAX_VALUE), String.valueOf(Short.MAX_VALUE),           String.valueOf(Byte.MAX_VALUE), "F"},//E,x
+  {   "F",   "y a b c d",     "2",           "3.14159f",     "g",     "B-1",   "H\u00C5T",        "-44",           "343.034435444", "-3", "0", "E"},//F,y
+  {   "G",   "x a b c d",     "3",           "-1.0",         "f",     "C-100", "sin",             "323254543543", "4.043544", "5", "100", "D"},//G,x
   {   "H",   "y a b c d",     "0",           "1.4E-45",      "e",     "C-88",  "H\u00D8T",        "1023423423005","4.043545", "10", "-50", "C"},//H,y
-	{   "I",   "x a b c d e f", "-2147483648", "1.0e+0",       "d",     "A-10",  "s\u00EDn",        "332422459999", "4.043546", "-340", "51", "B"},//I,x
-	{   "J",   "y a b c d e f", "4",           ".5",           "b",     "C-7",   "HOT",             "34334543543",  "4.0000220343", "300", "2", "A"},//J,y
-	{   "W",   "g",             "1",           null,           null,    null,    null,              null,           null, null, null, null},
-	{   "X",   "g",             "1",           "0.1",          null,    null,    null,              null,           null, null, null, null},
-	{   "Y",   "g",             "1",           "0.2",          null,    null,    null,              null,           null, null, null, null},
-	{   "Z",   "f g",           null,          null,           null,    null,    null,              null,           null, null, null, null}
- 	};
-
-	// create an index of all the documents, or just the x, or just the y documents
-	private Searcher getIndex (boolean even, boolean odd)
-	throws IOException {
-		RAMDirectory indexStore = new RAMDirectory ();
-		IndexWriter writer = new IndexWriter (indexStore, new SimpleAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
-		for (int i=0; i<data.length; ++i) {
-			if (((i%2)==0 && even) || ((i%2)==1 && odd)) {
-				Document doc = new Document();
-				doc.add (new Field ("tracer",   data[i][0], Field.Store.YES, Field.Index.NO));
-				doc.add (new Field ("contents", data[i][1], Field.Store.NO, Field.Index.ANALYZED));
-				if (data[i][2] != null) doc.add (new Field ("int",      data[i][2], Field.Store.NO, Field.Index.NOT_ANALYZED));
-				if (data[i][3] != null) doc.add (new Field ("float",    data[i][3], Field.Store.NO, Field.Index.NOT_ANALYZED));
-				if (data[i][4] != null) doc.add (new Field ("string",   data[i][4], Field.Store.NO, Field.Index.NOT_ANALYZED));
-				if (data[i][5] != null) doc.add (new Field ("custom",   data[i][5], Field.Store.NO, Field.Index.NOT_ANALYZED));
-				if (data[i][6] != null) doc.add (new Field ("i18n",     data[i][6], Field.Store.NO, Field.Index.NOT_ANALYZED));
+  {   "I",   "x a b c d e f", "-2147483648", "1.0e+0",       "d",     "A-10",  "s\u00EDn",        "332422459999", "4.043546", "-340", "51", "B"},//I,x
+  {   "J",   "y a b c d e f", "4",           ".5",           "b",     "C-7",   "HOT",             "34334543543",  "4.0000220343", "300", "2", "A"},//J,y
+  {   "W",   "g",             "1",           null,           null,    null,    null,              null,           null, null, null, null},
+  {   "X",   "g",             "1",           "0.1",          null,    null,    null,              null,           null, null, null, null},
+  {   "Y",   "g",             "1",           "0.2",          null,    null,    null,              null,           null, null, null, null},
+  {   "Z",   "f g",           null,          null,           null,    null,    null,              null,           null, null, null, null}
+  };
+
+  // create an index of all the documents, or just the x, or just the y documents
+  private Searcher getIndex (boolean even, boolean odd)
+  throws IOException {
+    RAMDirectory indexStore = new RAMDirectory ();
+    IndexWriter writer = new IndexWriter (indexStore, new SimpleAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
+    writer.setMaxBufferedDocs(2);
+    writer.setMergeFactor(1000);
+    for (int i=0; i<data.length; ++i) {
+      if (((i%2)==0 && even) || ((i%2)==1 && odd)) {
+        Document doc = new Document();
+        doc.add (new Field ("tracer",   data[i][0], Field.Store.YES, Field.Index.NO));
+        doc.add (new Field ("contents", data[i][1], Field.Store.NO, Field.Index.ANALYZED));
+        if (data[i][2] != null) doc.add (new Field ("int",      data[i][2], Field.Store.NO, Field.Index.NOT_ANALYZED));
+        if (data[i][3] != null) doc.add (new Field ("float",    data[i][3], Field.Store.NO, Field.Index.NOT_ANALYZED));
+        if (data[i][4] != null) doc.add (new Field ("string",   data[i][4], Field.Store.NO, Field.Index.NOT_ANALYZED));
+        if (data[i][5] != null) doc.add (new Field ("custom",   data[i][5], Field.Store.NO, Field.Index.NOT_ANALYZED));
+        if (data[i][6] != null) doc.add (new Field ("i18n",     data[i][6], Field.Store.NO, Field.Index.NOT_ANALYZED));
         if (data[i][7] != null) doc.add (new Field ("long",     data[i][7], Field.Store.NO, Field.Index.NOT_ANALYZED));
         if (data[i][8] != null) doc.add (new Field ("double",     data[i][8], Field.Store.NO, Field.Index.NOT_ANALYZED));
         if (data[i][9] != null) doc.add (new Field ("short",     data[i][9], Field.Store.NO, Field.Index.NOT_ANALYZED));
         if (data[i][10] != null) doc.add (new Field ("byte",     data[i][10], Field.Store.NO, Field.Index.NOT_ANALYZED));
         if (data[i][11] != null) doc.add (new Field ("parser",     data[i][11], Field.Store.NO, Field.Index.NOT_ANALYZED));
         doc.setBoost(2);  // produce some scores above 1.0
-				writer.addDocument (doc);
-			}
-		}
-		writer.optimize ();
-		writer.close ();
-		return new IndexSearcher (indexStore);
-	}
-
-	private Searcher getFullIndex()
-	throws IOException {
-		return getIndex (true, true);
-	}
-
-	private Searcher getXIndex()
-	throws IOException {
-		return getIndex (true, false);
-	}
-
-	private Searcher getYIndex()
-	throws IOException {
-		return getIndex (false, true);
-	}
-
-	private Searcher getEmptyIndex()
-	throws IOException {
-		return getIndex (false, false);
-	}
-
-	public void setUp() throws Exception {
-		full = getFullIndex();
-		searchX = getXIndex();
-		searchY = getYIndex();
-		queryX = new TermQuery (new Term ("contents", "x"));
-		queryY = new TermQuery (new Term ("contents", "y"));
-		queryA = new TermQuery (new Term ("contents", "a"));
+        writer.addDocument (doc);
+      }
+    }
+    //writer.optimize ();
+    writer.close ();
+    return new IndexSearcher (indexStore);
+  }
+
+  private Searcher getFullIndex()
+  throws IOException {
+    return getIndex (true, true);
+  }
+  
+  private IndexSearcher getFullStrings() throws CorruptIndexException, LockObtainFailedException, IOException {
+    RAMDirectory indexStore = new RAMDirectory ();
+    IndexWriter writer = new IndexWriter (indexStore, new SimpleAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
+    writer.setMaxBufferedDocs(4);
+    writer.setMergeFactor(97);
+    for (int i=0; i<NUM_STRINGS; i++) {
+        Document doc = new Document();
+        String num = getRandomCharString(getRandomNumber(2, 8), 48, 52);
+        doc.add (new Field ("tracer", num, Field.Store.YES, Field.Index.NO));
+        //doc.add (new Field ("contents", Integer.toString(i), Field.Store.NO, Field.Index.ANALYZED));
+        doc.add (new Field ("string", num, Field.Store.NO, Field.Index.NOT_ANALYZED));
+        String num2 = getRandomCharString(getRandomNumber(1, 4), 48, 50);
+        doc.add (new Field ("string2", num2, Field.Store.NO, Field.Index.NOT_ANALYZED));
+        doc.add (new Field ("tracer2", num2, Field.Store.YES, Field.Index.NO));
+        doc.setBoost(2);  // produce some scores above 1.0
+        writer.setMaxBufferedDocs(getRandomNumber(2, 12));
+        writer.addDocument (doc);
+      
+    }
+    //writer.optimize ();
+    //System.out.println(writer.getSegmentCount());
+    writer.close ();
+    return new IndexSearcher (indexStore);
+  }
+  
+  public static String getRandomNumberString(int num, int low, int high) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < num; i++) {
+      sb.append(getRandomNumber(low, high));
+    }
+    return sb.toString();
+  }
+  
+  public static String getRandomCharString(int num) {
+    return getRandomCharString(num, 48, 122);
+  }
+  
+  public static String getRandomCharString(int num, int start, int end) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < num; i++) {
+      sb.append(new Character((char) getRandomNumber(start, end)));
+    }
+    return sb.toString();
+  }
+  
+  static Random r = new Random();
+  
+  public static int getRandomNumber(final int low, final int high) {
+  
+    int randInt = (Math.abs(r.nextInt()) % (high - low)) + low;
+
+    return randInt;
+  }
+
+  private Searcher getXIndex()
+  throws IOException {
+    return getIndex (true, false);
+  }
+
+  private Searcher getYIndex()
+  throws IOException {
+    return getIndex (false, true);
+  }
+
+  private Searcher getEmptyIndex()
+  throws IOException {
+    return getIndex (false, false);
+  }
+
+  public void setUp() throws Exception {
+    super.setUp();
+    full = getFullIndex();
+    searchX = getXIndex();
+    searchY = getYIndex();
+    queryX = new TermQuery (new Term ("contents", "x"));
+    queryY = new TermQuery (new Term ("contents", "y"));
+    queryA = new TermQuery (new Term ("contents", "a"));
     queryE = new TermQuery (new Term ("contents", "e"));
-		queryF = new TermQuery (new Term ("contents", "f"));
-		queryG = new TermQuery (new Term ("contents", "g"));
-		sort = new Sort();
-	}
-
-	// test the sorts by score and document number
-	public void testBuiltInSorts() throws Exception {
-		sort = new Sort();
-		assertMatches (full, queryX, sort, "ACEGI");
-		assertMatches (full, queryY, sort, "BDFHJ");
-
-		sort.setSort(SortField.FIELD_DOC);
-		assertMatches (full, queryX, sort, "ACEGI");
-		assertMatches (full, queryY, sort, "BDFHJ");
-	}
-
-	// test sorts where the type of field is specified
-	public void testTypedSort() throws Exception {
-		sort.setSort (new SortField[] { new SortField ("int", SortField.INT), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "IGAEC");
-		assertMatches (full, queryY, sort, "DHFJB");
-
-		sort.setSort (new SortField[] { new SortField ("float", SortField.FLOAT), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "GCIEA");
-		assertMatches (full, queryY, sort, "DHJFB");
+    queryF = new TermQuery (new Term ("contents", "f"));
+    queryG = new TermQuery (new Term ("contents", "g"));
+    sort = new Sort();
+  }
+
+  // test the sorts by score and document number
+  public void testBuiltInSorts() throws Exception {
+    sort = new Sort();
+    assertMatches (full, queryX, sort, "ACEGI");
+    assertMatches (full, queryY, sort, "BDFHJ");
+
+    sort.setSort(SortField.FIELD_DOC);
+    assertMatches (full, queryX, sort, "ACEGI");
+    assertMatches (full, queryY, sort, "BDFHJ");
+  }
+
+  // test sorts where the type of field is specified
+  public void testTypedSort() throws Exception {
+    sort.setSort (new SortField[] { new SortField ("int", SortField.INT), SortField.FIELD_DOC });
+    assertMatches (full, queryX, sort, "IGAEC");
+    assertMatches (full, queryY, sort, "DHFJB");
+
+    sort.setSort (new SortField[] { new SortField ("float", SortField.FLOAT), SortField.FIELD_DOC });
+    assertMatches (full, queryX, sort, "GCIEA");
+    assertMatches (full, queryY, sort, "DHJFB");
 
     sort.setSort (new SortField[] { new SortField ("long", SortField.LONG), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "EACGI");
-		assertMatches (full, queryY, sort, "FBJHD");
+    assertMatches (full, queryX, sort, "EACGI");
+    assertMatches (full, queryY, sort, "FBJHD");
 
     sort.setSort (new SortField[] { new SortField ("double", SortField.DOUBLE), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "AGICE");
-		assertMatches (full, queryY, sort, "DJHBF");
+    assertMatches (full, queryX, sort, "AGICE");
+    assertMatches (full, queryY, sort, "DJHBF");
 
     sort.setSort (new SortField[] { new SortField ("byte", SortField.BYTE), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "CIGAE");
-		assertMatches (full, queryY, sort, "DHFBJ");
+    assertMatches (full, queryX, sort, "CIGAE");
+    assertMatches (full, queryY, sort, "DHFBJ");
 
     sort.setSort (new SortField[] { new SortField ("short", SortField.SHORT), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "IAGCE");
-		assertMatches (full, queryY, sort, "DFHBJ");
+    assertMatches (full, queryX, sort, "IAGCE");
+    assertMatches (full, queryY, sort, "DFHBJ");
 
     sort.setSort (new SortField[] { new SortField ("string", SortField.STRING), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "AIGEC");
-		assertMatches (full, queryY, sort, "DJHFB");
-	}
-
-	// test sorts where the type of field is specified and a custom field parser is used, that
-	// uses a simple char encoding. The sorted string contains a character beginning from 'A' that
-	// is mapped to a numeric value using some "funny" algorithm to be different for each data type.
-	public void testCustomFieldParserSort() throws Exception {
-		sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.IntParser(){
-			public final int parseInt(final String val) {
-				return (int) (val.charAt(0)-'A') * 123456;
-			}
-		}), SortField.FIELD_DOC });
-		assertMatches (full, queryA, sort, "JIHGFEDCBA");
-
-		sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.FloatParser(){
-			public final float parseFloat(final String val) {
-				return (float) Math.sqrt( (double) val.charAt(0) );
-			}
-		}), SortField.FIELD_DOC });
-		assertMatches (full, queryA, sort, "JIHGFEDCBA");
-
-		sort.setSort (new SortField[] { new SortField ("parser", new ExtendedFieldCache.LongParser(){
-			public final long parseLong(final String val) {
-				return (long) (val.charAt(0)-'A') * 1234567890L;
-			}
-		}), SortField.FIELD_DOC });
-		assertMatches (full, queryA, sort, "JIHGFEDCBA");
-
-		sort.setSort (new SortField[] { new SortField ("parser", new ExtendedFieldCache.DoubleParser(){
-			public final double parseDouble(final String val) {
-				return Math.pow( (double) val.charAt(0), (double) (val.charAt(0)-'A') );
-			}
-		}), SortField.FIELD_DOC });
-		assertMatches (full, queryA, sort, "JIHGFEDCBA");
-
-		sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.ByteParser(){
-			public final byte parseByte(final String val) {
-				return (byte) (val.charAt(0)-'A');
-			}
-		}), SortField.FIELD_DOC });
-		assertMatches (full, queryA, sort, "JIHGFEDCBA");
-
-		sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.ShortParser(){
-			public final short parseShort(final String val) {
-				return (short) (val.charAt(0)-'A');
-			}
-		}), SortField.FIELD_DOC });
-		assertMatches (full, queryA, sort, "JIHGFEDCBA");
-	}
-
-	// test sorts when there's nothing in the index
-	public void testEmptyIndex() throws Exception {
-		Searcher empty = getEmptyIndex();
-
-		sort = new Sort();
-		assertMatches (empty, queryX, sort, "");
-
-		sort.setSort(SortField.FIELD_DOC);
-		assertMatches (empty, queryX, sort, "");
-
-		sort.setSort (new SortField[] { new SortField ("int", SortField.INT), SortField.FIELD_DOC });
-		assertMatches (empty, queryX, sort, "");
-
-		sort.setSort (new SortField[] { new SortField ("string", SortField.STRING, true), SortField.FIELD_DOC });
-		assertMatches (empty, queryX, sort, "");
-
-		sort.setSort (new SortField[] { new SortField ("float", SortField.FLOAT), new SortField ("string", SortField.STRING) });
-		assertMatches (empty, queryX, sort, "");
-	}
-
-	// test sorts where the type of field is determined dynamically
-	public void testAutoSort() throws Exception {
-		sort.setSort("int");
-		assertMatches (full, queryX, sort, "IGAEC");
-		assertMatches (full, queryY, sort, "DHFJB");
-
-		sort.setSort("float");
-		assertMatches (full, queryX, sort, "GCIEA");
-		assertMatches (full, queryY, sort, "DHJFB");
-
-		sort.setSort("string");
-		assertMatches (full, queryX, sort, "AIGEC");
-		assertMatches (full, queryY, sort, "DJHFB");
-	}
-
-	// test sorts in reverse
-	public void testReverseSort() throws Exception {
-		sort.setSort (new SortField[] { new SortField (null, SortField.SCORE, true), SortField.FIELD_DOC });
-		assertMatches (full, queryX, sort, "IEGCA");
-		assertMatches (full, queryY, sort, "JFHDB");
-
-		sort.setSort (new SortField (null, SortField.DOC, true));
-		assertMatches (full, queryX, sort, "IGECA");
-		assertMatches (full, queryY, sort, "JHFDB");
-
-		sort.setSort ("int", true);
-		assertMatches (full, queryX, sort, "CAEGI");
-		assertMatches (full, queryY, sort, "BJFHD");
-
-		sort.setSort ("float", true);
-		assertMatches (full, queryX, sort, "AECIG");
-		assertMatches (full, queryY, sort, "BFJHD");
-
-		sort.setSort ("string", true);
-		assertMatches (full, queryX, sort, "CEGIA");
-		assertMatches (full, queryY, sort, "BFHJD");
-	}
-
-	// test sorting when the sort field is empty (undefined) for some of the documents
-	public void testEmptyFieldSort() throws Exception {
-		sort.setSort ("string");
-		assertMatches (full, queryF, sort, "ZJI");
-
-		sort.setSort ("string", true);
-		assertMatches (full, queryF, sort, "IJZ");
-		
-		sort.setSort (new SortField ("i18n", Locale.ENGLISH));
-		assertMatches (full, queryF, sort, "ZJI");
-		
-		sort.setSort (new SortField ("i18n", Locale.ENGLISH, true));
-		assertMatches (full, queryF, sort, "IJZ");
-
-		sort.setSort ("int");
-		assertMatches (full, queryF, sort, "IZJ");
-
-		sort.setSort ("int", true);
-		assertMatches (full, queryF, sort, "JZI");
-
-		sort.setSort ("float");
-		assertMatches (full, queryF, sort, "ZJI");
-
-		// using a nonexisting field as first sort key shouldn't make a difference:
-		sort.setSort (new SortField[] { new SortField ("nosuchfield", SortField.STRING),
-				new SortField ("float") });
-		assertMatches (full, queryF, sort, "ZJI");
+    assertMatches (full, queryX, sort, "AIGEC");
+    assertMatches (full, queryY, sort, "DJHFB");
+  }
+  
+  /**
+   * Test String sorting: small queue to many matches, multi field sort, reverse sort
+   */
+  public void testStringSort() throws IOException, ParseException {
+    ScoreDoc[] result = null;
+    IndexSearcher searcher = getFullStrings();
+    sort.setSort(new SortField[] {
+        new SortField("string", SortField.STRING),
+        new SortField("string2", SortField.STRING, true),
+        SortField.FIELD_DOC });
+
+    result = searcher.search(new MatchAllDocsQuery(), null, 500, sort).scoreDocs;
+
+    StringBuffer buff = new StringBuffer();
+    int n = result.length;
+    String last = null;
+    String lastSub = null;
+    int lastDocId = 0;
+    boolean fail = false;
+    for (int x = 0; x < n; ++x) {
+      Document doc2 = searcher.doc(result[x].doc);
+      String[] v = doc2.getValues("tracer");
+      String[] v2 = doc2.getValues("tracer2");
+      for (int j = 0; j < v.length; ++j) {
+        if (last != null) {
+          int cmp = v[j].compareTo(last);
+          if (!(cmp >= 0)) { // ensure first field is in order
+            fail = true;
+            System.out.println("fail:" + v[j] + " < " + last);
+          }
+          if (cmp == 0) { // ensure second field is in reverse order
+            cmp = v2[j].compareTo(lastSub);
+            if (cmp > 0) {
+              fail = true;
+              System.out.println("rev field fail:" + v2[j] + " > " + lastSub);
+            } else if(cmp == 0) { // ensure docid is in order
+              if (result[x].doc < lastDocId) {
+                fail = true;
+                System.out.println("doc fail:" + result[x].doc + " > " + lastDocId);
+              }
+            }
+          }
+        }
+        last = v[j];
+        lastSub = v2[j];
+        lastDocId = result[x].doc;
+        buff.append(v[j] + "(" + v2[j] + ")(" + result[x].doc+") ");
+      }
+    }
+    if(fail) {
+      System.out.println("topn field1(field2)(docID):" + buff);
+    }
+    assertFalse("Found sort results out of order", fail);
+
+  }
+  
+  // test sorts where the type of field is specified and a custom field parser is used, that
+  // uses a simple char encoding. The sorted string contains a character beginning from 'A' that
+  // is mapped to a numeric value using some "funny" algorithm to be different for each data type.
+  public void testCustomFieldParserSort() throws Exception {
+    sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.IntParser(){
+      public final int parseInt(final String val) {
+        return (int) (val.charAt(0)-'A') * 123456;
+      }
+    }), SortField.FIELD_DOC });
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+
+    sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.FloatParser(){
+      public final float parseFloat(final String val) {
+        return (float) Math.sqrt( (double) val.charAt(0) );
+      }
+    }), SortField.FIELD_DOC });
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+
+    sort.setSort (new SortField[] { new SortField ("parser", new ExtendedFieldCache.LongParser(){
+      public final long parseLong(final String val) {
+        return (long) (val.charAt(0)-'A') * 1234567890L;
+      }
+    }), SortField.FIELD_DOC });
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+
+    sort.setSort (new SortField[] { new SortField ("parser", new ExtendedFieldCache.DoubleParser(){
+      public final double parseDouble(final String val) {
+        return Math.pow( (double) val.charAt(0), (double) (val.charAt(0)-'A') );
+      }
+    }), SortField.FIELD_DOC });
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+
+    sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.ByteParser(){
+      public final byte parseByte(final String val) {
+        return (byte) (val.charAt(0)-'A');
+      }
+    }), SortField.FIELD_DOC });
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+
+    sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.ShortParser(){
+      public final short parseShort(final String val) {
+        return (short) (val.charAt(0)-'A');
+      }
+    }), SortField.FIELD_DOC });
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+  }
+
+  // test sorts when there's nothing in the index
+  public void testEmptyIndex() throws Exception {
+    Searcher empty = getEmptyIndex();
+
+    sort = new Sort();
+    assertMatches (empty, queryX, sort, "");
+
+    sort.setSort(SortField.FIELD_DOC);
+    assertMatches (empty, queryX, sort, "");
+
+    sort.setSort (new SortField[] { new SortField ("int", SortField.INT), SortField.FIELD_DOC });
+    assertMatches (empty, queryX, sort, "");
+
+    sort.setSort (new SortField[] { new SortField ("string", SortField.STRING, true), SortField.FIELD_DOC });
+    assertMatches (empty, queryX, sort, "");
+
+    sort.setSort (new SortField[] { new SortField ("float", SortField.FLOAT), new SortField ("string", SortField.STRING) });
+    assertMatches (empty, queryX, sort, "");
+  }
+
+  static class MyFieldComparator extends FieldComparator {
+    int[] docValues;
+    int[] slotValues;
+    int bottomValue;
+
+    MyFieldComparator(int numHits) {
+      slotValues = new int[numHits];
+    }
+
+    public void copy(int slot, int doc, float score) {
+      slotValues[slot] = docValues[doc];
+    }
+
+    public int compare(int slot1, int slot2) {
+      return slotValues[slot1] - slotValues[slot2];
+    }
+
+    public int compareBottom(int doc, float score) {
+      return bottomValue - docValues[doc];
+    }
+
+    public void setBottom(int bottom) {
+      bottomValue = slotValues[bottom];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
+      docValues = FieldCache.DEFAULT.getInts(reader, "parser", new FieldCache.IntParser() {
+          public final int parseInt(final String val) {
+            return (int) (val.charAt(0)-'A') * 123456;
+          }
+        });
+    }
+
+    public int sortType() {
+      return 0;
+    }
+
+    public Comparable value(int slot) {
+      return new Integer(slotValues[slot]);
+    }
+  }
+
+  static class MyFieldComparatorSource extends FieldComparatorSource {
+    FieldComparator newComparator(String fieldname, IndexReader[] subReaders, int numHits, int sortPos, boolean reversed) {
+      return new MyFieldComparator(numHits);
+    }
+  }
+
+  // Test sorting w/ custom FieldComparator
+  public void testNewCustomFieldParserSort() throws Exception {
+    sort.setSort (new SortField[] { new SortField ("parser", new MyFieldComparatorSource())});
+    assertMatches (full, queryA, sort, "JIHGFEDCBA");
+  }
+
+  // test sorts where the type of field is determined dynamically
+  public void testAutoSort() throws Exception {
+    sort.setSort("int");
+    assertMatches (full, queryX, sort, "IGAEC");
+    assertMatches (full, queryY, sort, "DHFJB");
+
+    sort.setSort("float");
+    assertMatches (full, queryX, sort, "GCIEA");
+    assertMatches (full, queryY, sort, "DHJFB");
+
+    sort.setSort("string");
+    assertMatches (full, queryX, sort, "AIGEC");
+    assertMatches (full, queryY, sort, "DJHFB");
+  }
+
+  // test sorts in reverse
+  public void testReverseSort() throws Exception {
+    sort.setSort (new SortField[] { new SortField (null, SortField.SCORE, true), SortField.FIELD_DOC });
+    assertMatches (full, queryX, sort, "IEGCA");
+    assertMatches (full, queryY, sort, "JFHDB");
+
+    sort.setSort (new SortField (null, SortField.DOC, true));
+    assertMatches (full, queryX, sort, "IGECA");
+    assertMatches (full, queryY, sort, "JHFDB");
+
+    sort.setSort ("int", true);
+    assertMatches (full, queryX, sort, "CAEGI");
+    assertMatches (full, queryY, sort, "BJFHD");
+
+    sort.setSort ("float", true);
+    assertMatches (full, queryX, sort, "AECIG");
+    assertMatches (full, queryY, sort, "BFJHD");
+
+    sort.setSort ("string", true);
+    assertMatches (full, queryX, sort, "CEGIA");
+    assertMatches (full, queryY, sort, "BFHJD");
+  }
+
+  // test sorting when the sort field is empty (undefined) for some of the documents
+  public void testEmptyFieldSort() throws Exception {
+    sort.setSort ("string");
+    assertMatches (full, queryF, sort, "ZJI");
+
+    sort.setSort ("string", true);
+    assertMatches (full, queryF, sort, "IJZ");
+    
+    sort.setSort (new SortField ("i18n", Locale.ENGLISH));
+    assertMatches (full, queryF, sort, "ZJI");
+    
+    sort.setSort (new SortField ("i18n", Locale.ENGLISH, true));
+    assertMatches (full, queryF, sort, "IJZ");
+
+    sort.setSort ("int");
+    assertMatches (full, queryF, sort, "IZJ");
+
+    sort.setSort ("int", true);
+    assertMatches (full, queryF, sort, "JZI");
 
-		sort.setSort ("float", true);
-		assertMatches (full, queryF, sort, "IJZ");
+    sort.setSort ("float");
+    assertMatches (full, queryF, sort, "ZJI");
 
-		// When a field is null for both documents, the next SortField should be used.
+    // using a nonexisting field as first sort key shouldn't make a difference:
+    sort.setSort (new SortField[] { new SortField ("nosuchfield", SortField.STRING),
+        new SortField ("float") });
+    assertMatches (full, queryF, sort, "ZJI");
+
+    sort.setSort ("float", true);
+    assertMatches (full, queryF, sort, "IJZ");
+
+    // When a field is null for both documents, the next SortField should be used.
                 // Works for
-		sort.setSort (new SortField[] { new SortField ("int"),
+    sort.setSort (new SortField[] { new SortField ("int"),
                                 new SortField ("string", SortField.STRING),
-				new SortField ("float") });
-		assertMatches (full, queryG, sort, "ZWXY");
+        new SortField ("float") });
+    assertMatches (full, queryG, sort, "ZWXY");
 
-		// Reverse the last criterium to make sure the test didn't pass by chance
-		sort.setSort (new SortField[] { new SortField ("int"),
+    // Reverse the last criterium to make sure the test didn't pass by chance
+    sort.setSort (new SortField[] { new SortField ("int"),
                                 new SortField ("string", SortField.STRING),
-				new SortField ("float", true) });
-		assertMatches (full, queryG, sort, "ZYXW");
+        new SortField ("float", true) });
+    assertMatches (full, queryG, sort, "ZYXW");
 
-		// Do the same for a MultiSearcher
-		Searcher multiSearcher=new MultiSearcher (new Searchable[] { full });
+    // Do the same for a MultiSearcher
+    Searcher multiSearcher=new MultiSearcher (new Searchable[] { full });
 
-		sort.setSort (new SortField[] { new SortField ("int"),
+    sort.setSort (new SortField[] { new SortField ("int"),
                                 new SortField ("string", SortField.STRING),
-				new SortField ("float") });
-		assertMatches (multiSearcher, queryG, sort, "ZWXY");
+        new SortField ("float") });
+    assertMatches (multiSearcher, queryG, sort, "ZWXY");
 
-		sort.setSort (new SortField[] { new SortField ("int"),
+    sort.setSort (new SortField[] { new SortField ("int"),
                                 new SortField ("string", SortField.STRING),
-				new SortField ("float", true) });
-		assertMatches (multiSearcher, queryG, sort, "ZYXW");
-		// Don't close the multiSearcher. it would close the full searcher too!
+        new SortField ("float", true) });
+    assertMatches (multiSearcher, queryG, sort, "ZYXW");
+    // Don't close the multiSearcher. it would close the full searcher too!
 
-		// Do the same for a ParallelMultiSearcher
+    // Do the same for a ParallelMultiSearcher
                 Searcher parallelSearcher=new ParallelMultiSearcher (new Searchable[] { full });
 
-		sort.setSort (new SortField[] { new SortField ("int"),
+    sort.setSort (new SortField[] { new SortField ("int"),
                                 new SortField ("string", SortField.STRING),
-				new SortField ("float") });
-		assertMatches (parallelSearcher, queryG, sort, "ZWXY");
+        new SortField ("float") });
+    assertMatches (parallelSearcher, queryG, sort, "ZWXY");
 
-		sort.setSort (new SortField[] { new SortField ("int"),
+    sort.setSort (new SortField[] { new SortField ("int"),
                                 new SortField ("string", SortField.STRING),
-				new SortField ("float", true) });
-		assertMatches (parallelSearcher, queryG, sort, "ZYXW");
-		// Don't close the parallelSearcher. it would close the full searcher too!
-	}
-
-	// test sorts using a series of fields
-	public void testSortCombos() throws Exception {
-		sort.setSort (new String[] {"int","float"});
-		assertMatches (full, queryX, sort, "IGEAC");
-
-		sort.setSort (new SortField[] { new SortField ("int", true), new SortField (null, SortField.DOC, true) });
-		assertMatches (full, queryX, sort, "CEAGI");
-
-		sort.setSort (new String[] {"float","string"});
-		assertMatches (full, queryX, sort, "GICEA");
-	}
-
-	// test using a Locale for sorting strings
-	public void testLocaleSort() throws Exception {
-		sort.setSort (new SortField[] { new SortField ("string", Locale.US) });
-		assertMatches (full, queryX, sort, "AIGEC");
-		assertMatches (full, queryY, sort, "DJHFB");
-
-		sort.setSort (new SortField[] { new SortField ("string", Locale.US, true) });
-		assertMatches (full, queryX, sort, "CEGIA");
-		assertMatches (full, queryY, sort, "BFHJD");
-	}
-
-	// test using various international locales with accented characters
-	// (which sort differently depending on locale)
-	public void testInternationalSort() throws Exception {
-		sort.setSort (new SortField ("i18n", Locale.US));
-		assertMatches (full, queryY, sort, "BFJDH");
-
-		sort.setSort (new SortField ("i18n", new Locale("sv", "se")));
-		assertMatches (full, queryY, sort, "BJDFH");
-
-		sort.setSort (new SortField ("i18n", new Locale("da", "dk")));
-		assertMatches (full, queryY, sort, "BJDHF");
-
-		sort.setSort (new SortField ("i18n", Locale.US));
-		assertMatches (full, queryX, sort, "ECAGI");
-
-		sort.setSort (new SortField ("i18n", Locale.FRANCE));
-		assertMatches (full, queryX, sort, "EACGI");
-	}
+        new SortField ("float", true) });
+    assertMatches (parallelSearcher, queryG, sort, "ZYXW");
+    // Don't close the parallelSearcher. it would close the full searcher too!
+  }
+
+  // test sorts using a series of fields
+  public void testSortCombos() throws Exception {
+    sort.setSort (new String[] {"int","float"});
+    assertMatches (full, queryX, sort, "IGEAC");
+
+    sort.setSort (new SortField[] { new SortField ("int", true), new SortField (null, SortField.DOC, true) });
+    assertMatches (full, queryX, sort, "CEAGI");
+
+    sort.setSort (new String[] {"float","string"});
+    assertMatches (full, queryX, sort, "GICEA");
+  }
+
+  // test using a Locale for sorting strings
+  public void testLocaleSort() throws Exception {
+    sort.setSort (new SortField[] { new SortField ("string", Locale.US) });
+    assertMatches (full, queryX, sort, "AIGEC");
+    assertMatches (full, queryY, sort, "DJHFB");
+
+    sort.setSort (new SortField[] { new SortField ("string", Locale.US, true) });
+    assertMatches (full, queryX, sort, "CEGIA");
+    assertMatches (full, queryY, sort, "BFHJD");
+  }
+
+  // test using various international locales with accented characters
+  // (which sort differently depending on locale)
+  public void testInternationalSort() throws Exception {
+    sort.setSort (new SortField ("i18n", Locale.US));
+    assertMatches (full, queryY, sort, "BFJDH");
+
+    sort.setSort (new SortField ("i18n", new Locale("sv", "se")));
+    assertMatches (full, queryY, sort, "BJDFH");
+
+    sort.setSort (new SortField ("i18n", new Locale("da", "dk")));
+    assertMatches (full, queryY, sort, "BJDHF");
+
+    sort.setSort (new SortField ("i18n", Locale.US));
+    assertMatches (full, queryX, sort, "ECAGI");
+
+    sort.setSort (new SortField ("i18n", Locale.FRANCE));
+    assertMatches (full, queryX, sort, "EACGI");
+  }
     
     // Test the MultiSearcher's ability to preserve locale-sensitive ordering
     // by wrapping it around a single searcher
-	public void testInternationalMultiSearcherSort() throws Exception {
-		Searcher multiSearcher = new MultiSearcher (new Searchable[] { full });
-		
-		sort.setSort (new SortField ("i18n", new Locale("sv", "se")));
-		assertMatches (multiSearcher, queryY, sort, "BJDFH");
-		
-		sort.setSort (new SortField ("i18n", Locale.US));
-		assertMatches (multiSearcher, queryY, sort, "BFJDH");
-		
-		sort.setSort (new SortField ("i18n", new Locale("da", "dk")));
-		assertMatches (multiSearcher, queryY, sort, "BJDHF");
-	} 
+  public void testInternationalMultiSearcherSort() throws Exception {
+    Searcher multiSearcher = new MultiSearcher (new Searchable[] { full });
+    
+    sort.setSort (new SortField ("i18n", new Locale("sv", "se")));
+    assertMatches (multiSearcher, queryY, sort, "BJDFH");
     
-	// test a custom sort function
-	public void testCustomSorts() throws Exception {
-		sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource()));
-		assertMatches (full, queryX, sort, "CAIEG");
-		sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource(), true));
-		assertMatches (full, queryY, sort, "HJDBF");
-		SortComparator custom = SampleComparable.getComparator();
-		sort.setSort (new SortField ("custom", custom));
-		assertMatches (full, queryX, sort, "CAIEG");
-		sort.setSort (new SortField ("custom", custom, true));
-		assertMatches (full, queryY, sort, "HJDBF");
-	}
-
-	// test a variety of sorts using more than one searcher
-	public void testMultiSort() throws Exception {
-		MultiSearcher searcher = new MultiSearcher (new Searchable[] { searchX, searchY });
-		runMultiSorts (searcher);
-	}
-
-	// test a variety of sorts using a parallel multisearcher
-	public void testParallelMultiSort() throws Exception {
-		Searcher searcher = new ParallelMultiSearcher (new Searchable[] { searchX, searchY });
-		runMultiSorts (searcher);
-	}
-
-	// test a variety of sorts using a remote searcher
-	public void testRemoteSort() throws Exception {
-		Searchable searcher = getRemote();
-		MultiSearcher multi = new MultiSearcher (new Searchable[] { searcher });
-		runMultiSorts (multi);
-	}
-
-	// test custom search when remote
-	public void testRemoteCustomSort() throws Exception {
-		Searchable searcher = getRemote();
-		MultiSearcher multi = new MultiSearcher (new Searchable[] { searcher });
-		sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource()));
-		assertMatches (multi, queryX, sort, "CAIEG");
-		sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource(), true));
-		assertMatches (multi, queryY, sort, "HJDBF");
-		SortComparator custom = SampleComparable.getComparator();
-		sort.setSort (new SortField ("custom", custom));
-		assertMatches (multi, queryX, sort, "CAIEG");
-		sort.setSort (new SortField ("custom", custom, true));
-		assertMatches (multi, queryY, sort, "HJDBF");
-	}
-
-	// test that the relevancy scores are the same even if
-	// hits are sorted
-	public void testNormalizedScores() throws Exception {
+    sort.setSort (new SortField ("i18n", Locale.US));
+    assertMatches (multiSearcher, queryY, sort, "BFJDH");
+    
+    sort.setSort (new SortField ("i18n", new Locale("da", "dk")));
+    assertMatches (multiSearcher, queryY, sort, "BJDHF");
+  } 
+    
+  // test a custom sort function
+  public void testCustomSorts() throws Exception {
+    sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource()));
+    assertMatches (full, queryX, sort, "CAIEG");
+    sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource(), true));
+    assertMatches (full, queryY, sort, "HJDBF");
+    SortComparator custom = SampleComparable.getComparator();
+    sort.setSort (new SortField ("custom", custom));
+    assertMatches (full, queryX, sort, "CAIEG");
+    sort.setSort (new SortField ("custom", custom, true));
+    assertMatches (full, queryY, sort, "HJDBF");
+  }
+
+  // test a variety of sorts using more than one searcher
+  public void testMultiSort() throws Exception {
+    MultiSearcher searcher = new MultiSearcher (new Searchable[] { searchX, searchY });
+    runMultiSorts (searcher);
+  }
+
+  // test a variety of sorts using a parallel multisearcher
+  public void testParallelMultiSort() throws Exception {
+    Searcher searcher = new ParallelMultiSearcher (new Searchable[] { searchX, searchY });
+    runMultiSorts (searcher);
+  }
 
-		// capture relevancy scores
+  // test a variety of sorts using a remote searcher
+  public void testRemoteSort() throws Exception {
+    Searchable searcher = getRemote();
+    MultiSearcher multi = new MultiSearcher (new Searchable[] { searcher });
+    runMultiSorts (multi);
+  }
+
+  // test custom search when remote
+  public void testRemoteCustomSort() throws Exception {
+    Searchable searcher = getRemote();
+    MultiSearcher multi = new MultiSearcher (new Searchable[] { searcher });
+    sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource()));
+    assertMatches (multi, queryX, sort, "CAIEG");
+    sort.setSort (new SortField ("custom", SampleComparable.getComparatorSource(), true));
+    assertMatches (multi, queryY, sort, "HJDBF");
+    SortComparator custom = SampleComparable.getComparator();
+    sort.setSort (new SortField ("custom", custom));
+    assertMatches (multi, queryX, sort, "CAIEG");
+    sort.setSort (new SortField ("custom", custom, true));
+    assertMatches (multi, queryY, sort, "HJDBF");
+  }
+
+  // test that the relevancy scores are the same even if
+  // hits are sorted
+  public void testNormalizedScores() throws Exception {
+
+    // capture relevancy scores
     HashMap scoresX = getScores (full.search (queryX, null, 1000).scoreDocs, full);
     HashMap scoresY = getScores (full.search (queryY, null, 1000).scoreDocs, full);
     HashMap scoresA = getScores (full.search (queryA, null, 1000).scoreDocs, full);
 
-		// we'll test searching locally, remote and multi
-		MultiSearcher remote = new MultiSearcher (new Searchable[] { getRemote() });
-		MultiSearcher multi  = new MultiSearcher (new Searchable[] { searchX, searchY });
+    // we'll test searching locally, remote and multi
+    MultiSearcher remote = new MultiSearcher (new Searchable[] { getRemote() });
+    MultiSearcher multi  = new MultiSearcher (new Searchable[] { searchX, searchY });
 
-		// change sorting and make sure relevancy stays the same
+    // change sorting and make sure relevancy stays the same
 
-		sort = new Sort();
+    sort = new Sort();
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -527,7 +698,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort(SortField.FIELD_DOC);
+    sort.setSort(SortField.FIELD_DOC);
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -538,7 +709,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort ("int");
+    sort.setSort ("int");
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -549,7 +720,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort ("float");
+    sort.setSort ("float");
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -560,7 +731,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort ("string");
+    sort.setSort ("string");
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -571,7 +742,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort (new String[] {"int","float"});
+    sort.setSort (new String[] {"int","float"});
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -582,7 +753,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort (new SortField[] { new SortField ("int", true), new SortField (null, SortField.DOC, true) });
+    sort.setSort (new SortField[] { new SortField ("int", true), new SortField (null, SortField.DOC, true) });
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -593,7 +764,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-		sort.setSort (new String[] {"float","string"});
+    sort.setSort (new String[] {"float","string"});
     assertSameValues (scoresX, getScores (full.search (queryX, null, 1000, sort).scoreDocs, full));
     assertSameValues (scoresX, getScores (remote.search (queryX, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresX, getScores (multi.search (queryX, null, 1000, sort).scoreDocs, multi));
@@ -604,7 +775,7 @@
     assertSameValues (scoresA, getScores (remote.search (queryA, null, 1000, sort).scoreDocs, remote));
     assertSameValues (scoresA, getScores (multi.search (queryA, null, 1000, sort).scoreDocs, multi));
 
-	}
+  }
 
   public void testTopDocsScores() throws Exception {
 
@@ -621,6 +792,7 @@
     Filter filt = new Filter() {
       public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
         BitSet bs = new BitSet(reader.maxDoc());
+        bs.set(0, reader.maxDoc());
         bs.set(docs1.scoreDocs[0].doc);
         return new DocIdBitSet(bs);
       }
@@ -631,121 +803,123 @@
     assertEquals(docs1.scoreDocs[0].score, docs2.scoreDocs[0].score, 1e-6);
   }
 
-
   // runs a variety of sorts useful for multisearchers
-	private void runMultiSorts (Searcher multi) throws Exception {
-		sort.setSort (SortField.FIELD_DOC);
-		assertMatchesPattern (multi, queryA, sort, "[AB]{2}[CD]{2}[EF]{2}[GH]{2}[IJ]{2}");
+  private void runMultiSorts (Searcher multi) throws Exception {
+    sort.setSort (SortField.FIELD_DOC);
+    assertMatchesPattern (multi, queryA, sort, "[AB]{2}[CD]{2}[EF]{2}[GH]{2}[IJ]{2}");
 
-		sort.setSort (new SortField ("int", SortField.INT));
-		assertMatchesPattern (multi, queryA, sort, "IDHFGJ[ABE]{3}C");
+    sort.setSort (new SortField ("int", SortField.INT));
+    assertMatchesPattern (multi, queryA, sort, "IDHFGJ[ABE]{3}C");
 
-		sort.setSort (new SortField[] {new SortField ("int", SortField.INT), SortField.FIELD_DOC});
-		assertMatchesPattern (multi, queryA, sort, "IDHFGJ[AB]{2}EC");
+    sort.setSort (new SortField[] {new SortField ("int", SortField.INT), SortField.FIELD_DOC});
+    assertMatchesPattern (multi, queryA, sort, "IDHFGJ[AB]{2}EC");
 
-		sort.setSort ("int");
-		assertMatchesPattern (multi, queryA, sort, "IDHFGJ[AB]{2}EC");
+    sort.setSort ("int");
+    assertMatchesPattern (multi, queryA, sort, "IDHFGJ[AB]{2}EC");
 
-		sort.setSort (new SortField[] {new SortField ("float", SortField.FLOAT), SortField.FIELD_DOC});
-		assertMatchesPattern (multi, queryA, sort, "GDHJ[CI]{2}EFAB");
+    sort.setSort (new SortField[] {new SortField ("float", SortField.FLOAT), SortField.FIELD_DOC});
+    assertMatchesPattern (multi, queryA, sort, "GDHJ[CI]{2}EFAB");
 
-		sort.setSort ("float");
-		assertMatchesPattern (multi, queryA, sort, "GDHJ[CI]{2}EFAB");
+    sort.setSort ("float");
+    assertMatchesPattern (multi, queryA, sort, "GDHJ[CI]{2}EFAB");
 
-		sort.setSort ("string");
-		assertMatches (multi, queryA, sort, "DJAIHGFEBC");
+    sort.setSort ("string");
+    assertMatches (multi, queryA, sort, "DJAIHGFEBC");
 
-		sort.setSort ("int", true);
-		assertMatchesPattern (multi, queryA, sort, "C[AB]{2}EJGFHDI");
+    sort.setSort ("int", true);
+    assertMatchesPattern (multi, queryA, sort, "C[AB]{2}EJGFHDI");
 
-		sort.setSort ("float", true);
-		assertMatchesPattern (multi, queryA, sort, "BAFE[IC]{2}JHDG");
+    sort.setSort ("float", true);
+    assertMatchesPattern (multi, queryA, sort, "BAFE[IC]{2}JHDG");
 
-		sort.setSort ("string", true);
-		assertMatches (multi, queryA, sort, "CBEFGHIAJD");
+    sort.setSort ("string", true);
+    assertMatches (multi, queryA, sort, "CBEFGHIAJD");
 
-		sort.setSort (new SortField[] { new SortField ("string", Locale.US) });
-		assertMatches (multi, queryA, sort, "DJAIHGFEBC");
+    sort.setSort (new SortField[] { new SortField ("string", Locale.US) });
+    assertMatches (multi, queryA, sort, "DJAIHGFEBC");
 
-		sort.setSort (new SortField[] { new SortField ("string", Locale.US, true) });
-		assertMatches (multi, queryA, sort, "CBEFGHIAJD");
+    sort.setSort (new SortField[] { new SortField ("string", Locale.US, true) });
+    assertMatches (multi, queryA, sort, "CBEFGHIAJD");
 
-		sort.setSort (new String[] {"int","float"});
-		assertMatches (multi, queryA, sort, "IDHFGJEABC");
+    sort.setSort (new String[] {"int","float"});
+    assertMatches (multi, queryA, sort, "IDHFGJEABC");
 
-		sort.setSort (new String[] {"float","string"});
-		assertMatches (multi, queryA, sort, "GDHJICEFAB");
+    sort.setSort (new String[] {"float","string"});
+    assertMatches (multi, queryA, sort, "GDHJICEFAB");
 
-		sort.setSort ("int");
-		assertMatches (multi, queryF, sort, "IZJ");
+    sort.setSort ("int");
+    assertMatches (multi, queryF, sort, "IZJ");
 
-		sort.setSort ("int", true);
-		assertMatches (multi, queryF, sort, "JZI");
+    sort.setSort ("int", true);
+    assertMatches (multi, queryF, sort, "JZI");
 
-		sort.setSort ("float");
-		assertMatches (multi, queryF, sort, "ZJI");
+    sort.setSort ("float");
+    assertMatches (multi, queryF, sort, "ZJI");
 
-		sort.setSort ("string");
-		assertMatches (multi, queryF, sort, "ZJI");
+    sort.setSort ("string");
+    assertMatches (multi, queryF, sort, "ZJI");
 
-		sort.setSort ("string", true);
-		assertMatches (multi, queryF, sort, "IJZ");
-	}
+    sort.setSort ("string", true);
+    assertMatches (multi, queryF, sort, "IJZ");
+  }
 
-	// make sure the documents returned by the search match the expected list
-	private void assertMatches (Searcher searcher, Query query, Sort sort, String expectedResult)
-	throws IOException {
-    ScoreDoc[] result = searcher.search (query, null, 1000, sort).scoreDocs;
-		StringBuffer buff = new StringBuffer(10);
+  // make sure the documents returned by the search match the expected list
+  private void assertMatches (Searcher searcher, Query query, Sort sort, String expectedResult)
+  throws IOException {
+    //ScoreDoc[] result = searcher.search (query, null, 1000, sort).scoreDocs;
+    TopDocs hits = searcher.search (query, null, expectedResult.length(), sort);
+    ScoreDoc[] result = hits.scoreDocs;
+    assertEquals(hits.totalHits, expectedResult.length());
+    StringBuffer buff = new StringBuffer(10);
     int n = result.length;
-		for (int i=0; i<n; ++i) {
+    for (int i=0; i<n; ++i) {
       Document doc = searcher.doc(result[i].doc);
-			String[] v = doc.getValues("tracer");
-			for (int j=0; j<v.length; ++j) {
-				buff.append (v[j]);
-			}
-		}
-		assertEquals (expectedResult, buff.toString());
-	}
-
-	// make sure the documents returned by the search match the expected list pattern
-	private void assertMatchesPattern (Searcher searcher, Query query, Sort sort, String pattern)
-	throws IOException {
+      String[] v = doc.getValues("tracer");
+      for (int j=0; j<v.length; ++j) {
+        buff.append (v[j]);
+      }
+    }
+    assertEquals (expectedResult, buff.toString());
+  }
+
+  // make sure the documents returned by the search match the expected list pattern
+  private void assertMatchesPattern (Searcher searcher, Query query, Sort sort, String pattern)
+  throws IOException {
     ScoreDoc[] result = searcher.search (query, null, 1000, sort).scoreDocs;
-		StringBuffer buff = new StringBuffer(10);
+    StringBuffer buff = new StringBuffer(10);
     int n = result.length;
-		for (int i=0; i<n; ++i) {
+    for (int i=0; i<n; ++i) {
       Document doc = searcher.doc(result[i].doc);
-			String[] v = doc.getValues("tracer");
-			for (int j=0; j<v.length; ++j) {
-				buff.append (v[j]);
-			}
-		}
-		// System.out.println ("matching \""+buff+"\" against pattern \""+pattern+"\"");
-		assertTrue (Pattern.compile(pattern).matcher(buff.toString()).matches());
-	}
+      String[] v = doc.getValues("tracer");
+      for (int j=0; j<v.length; ++j) {
+        buff.append (v[j]);
+      }
+    }
+    // System.out.println ("matching \""+buff+"\" against pattern \""+pattern+"\"");
+    assertTrue (Pattern.compile(pattern).matcher(buff.toString()).matches());
+  }
 
   private HashMap getScores (ScoreDoc[] hits, Searcher searcher)
-	throws IOException {
-		HashMap scoreMap = new HashMap();
+  throws IOException {
+    HashMap scoreMap = new HashMap();
     int n = hits.length;
-		for (int i=0; i<n; ++i) {
+    for (int i=0; i<n; ++i) {
       Document doc = searcher.doc(hits[i].doc);
-			String[] v = doc.getValues("tracer");
-			assertEquals (v.length, 1);
+      String[] v = doc.getValues("tracer");
+      assertEquals (v.length, 1);
       scoreMap.put (v[0], new Float(hits[i].score));
-		}
-		return scoreMap;
-	}
-
-	// make sure all the values in the maps match
-	private void assertSameValues (HashMap m1, HashMap m2) {
-		int n = m1.size();
-		int m = m2.size();
-		assertEquals (n, m);
-		Iterator iter = m1.keySet().iterator();
-		while (iter.hasNext()) {
-			Object key = iter.next();
+    }
+    return scoreMap;
+  }
+
+  // make sure all the values in the maps match
+  private void assertSameValues (HashMap m1, HashMap m2) {
+    int n = m1.size();
+    int m = m2.size();
+    assertEquals (n, m);
+    Iterator iter = m1.keySet().iterator();
+    while (iter.hasNext()) {
+      Object key = iter.next();
       Object o1 = m1.get(key);
       Object o2 = m2.get(key);
       if (o1 instanceof Float) {
@@ -753,31 +927,31 @@
       } else {
         assertEquals (m1.get(key), m2.get(key));
       }
-		}
-	}
+    }
+  }
+
+  private Searchable getRemote () throws Exception {
+    try {
+      return lookupRemote ();
+    } catch (Throwable e) {
+      startServer ();
+      return lookupRemote ();
+    }
+  }
 
-	private Searchable getRemote () throws Exception {
-		try {
-			return lookupRemote ();
-		} catch (Throwable e) {
-			startServer ();
-			return lookupRemote ();
-		}
-	}
-
-	private Searchable lookupRemote () throws Exception {
-		return (Searchable) Naming.lookup ("//localhost/SortedSearchable");
-	}
-
-	private void startServer () throws Exception {
-		// construct an index
-		Searcher local = getFullIndex();
-		// local.search (queryA, new Sort());
-
-		// publish it
-		Registry reg = LocateRegistry.createRegistry (1099);
-		RemoteSearchable impl = new RemoteSearchable (local);
-		Naming.rebind ("//localhost/SortedSearchable", impl);
-	}
+  private Searchable lookupRemote () throws Exception {
+    return (Searchable) Naming.lookup ("//localhost/SortedSearchable");
+  }
+
+  private void startServer () throws Exception {
+    // construct an index
+    Searcher local = getFullIndex();
+    // local.search (queryA, new Sort());
+
+    // publish it
+    LocateRegistry.createRegistry (1099);
+    RemoteSearchable impl = new RemoteSearchable (local);
+    Naming.rebind ("//localhost/SortedSearchable", impl);
+  }
 
 }

Added: lucene/java/trunk/src/test/org/apache/lucene/search/TestStressSort.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestStressSort.java?rev=738219&view=auto
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestStressSort.java (added)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestStressSort.java Tue Jan 27 20:15:21 2009
@@ -0,0 +1,354 @@
+package org.apache.lucene.search;
+
+/**
+ * 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 org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.store.MockRAMDirectory;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+import java.util.Random;
+import java.util.Arrays;
+
+public class TestStressSort extends LuceneTestCase {
+
+  private final static int NUM_DOCS = 5000;
+  // NOTE: put seed in here to make failures
+  // deterministic, but do not commit with a seed (to
+  // better test):
+  private final Random r = new Random();
+  private Directory dir, dir2, dir3;
+  private IndexSearcher searcherMultiSegment;
+  private IndexSearcher searcherFewSegment;
+  private IndexSearcher searcherSingleSegment;
+
+  private static final boolean VERBOSE = false;
+
+  // min..max
+  private int nextInt(int min, int max) {
+    return min + r.nextInt(max-min+1);
+  }
+
+  // 0..(lim-1)
+  private int nextInt(int lim) {
+    return r.nextInt(lim);
+  }
+
+  final char[] buffer = new char[20];
+  private String randomString(int size) {
+    assert size < 20;
+    for(int i=0;i<size;i++) {
+      buffer[i] = (char) nextInt(48, 122);
+    }
+    return new String(buffer, 0, size);
+  }
+
+  private void create() throws Throwable {
+
+    // NOTE: put seed in here to make failures
+    // deterministic, but do not commit with a seed (to
+    // better test):
+    final Random r = new Random();
+    dir = new MockRAMDirectory();
+    IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
+    writer.setMaxBufferedDocs(17);
+
+    final Document doc = new Document();
+    final Document doc2 = new Document();
+
+    final Field id = new Field("id", "", Field.Store.YES, Field.Index.NO);
+    doc.add(id);
+    doc2.add(id);
+
+    final Field contents = new Field("contents", "", Field.Store.NO, Field.Index.ANALYZED);
+    doc.add(contents);
+    doc2.add(contents);
+
+    final Field byteField = new Field("byte", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(byteField);
+    doc2.add(byteField);
+
+    final Field shortField = new Field("short", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(shortField);
+    doc2.add(shortField);
+
+    final Field intField = new Field("int", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(intField);
+    doc2.add(intField);
+
+    final Field longField = new Field("long", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(longField);
+    doc2.add(longField);
+
+    final Field floatField = new Field("float", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(floatField);
+    doc2.add(floatField);
+
+    final Field doubleField = new Field("double", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(doubleField);
+    doc2.add(doubleField);
+
+    final Field stringField = new Field("string", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
+    doc.add(stringField);
+    // doc2 doesn't have stringField, so we get nulls
+
+    for(int i=0;i<NUM_DOCS;i++) {
+      id.setValue(""+i);
+      if (i % 1000 == 0) {
+        contents.setValue("a b c z");
+      } else if (i % 100 == 0) {
+        contents.setValue("a b c y");
+      } else if (i % 10 == 0) {
+        contents.setValue("a b c x");
+      } else {
+        contents.setValue("a b c");
+      }
+      byteField.setValue(""+nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE));
+      if (nextInt(10) == 3) {
+        shortField.setValue(""+Short.MIN_VALUE);
+      } else if (nextInt(10) == 7) {
+        shortField.setValue(""+Short.MAX_VALUE);
+      } else {
+        shortField.setValue(""+nextInt(Short.MIN_VALUE, Short.MAX_VALUE));
+      }
+
+      if (nextInt(10) == 3) {
+        intField.setValue(""+Integer.MIN_VALUE);
+      } else if (nextInt(10) == 7) {
+        intField.setValue(""+Integer.MAX_VALUE);
+      } else {
+        intField.setValue(""+r.nextInt());
+      }
+
+      if (nextInt(10) == 3) {
+        longField.setValue(""+Long.MIN_VALUE);
+      } else if (nextInt(10) == 7) {
+        longField.setValue(""+Long.MAX_VALUE);
+      } else {
+        longField.setValue(""+r.nextLong());
+      }
+      floatField.setValue(""+r.nextFloat());
+      doubleField.setValue(""+r.nextDouble());
+      if (i % 197 == 0) {
+        writer.addDocument(doc2);
+      } else {
+        stringField.setValue(randomString(nextInt(20)));
+        writer.addDocument(doc);
+      }
+    }
+    writer.close();
+    searcherMultiSegment = new IndexSearcher(dir);
+
+    dir2 = new MockRAMDirectory(dir);
+    writer = new IndexWriter(dir2, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
+    writer.optimize();
+    writer.close();
+    searcherSingleSegment = new IndexSearcher(dir2);
+
+    dir3 = new MockRAMDirectory(dir);
+    writer = new IndexWriter(dir3, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
+    writer.optimize(3);
+    writer.close();
+    searcherFewSegment = new IndexSearcher(dir3);
+  }
+
+  private void close() throws Throwable {
+    searcherMultiSegment.close();
+    searcherFewSegment.close();
+    searcherSingleSegment.close();
+    dir.close();
+    dir2.close();
+  }
+
+  public void testSort() throws Throwable {
+
+    // reverse & not
+    // all types
+    // restrictive & non restrictive searches (on contents)
+
+    create();
+
+    Sort[] sorts = new Sort[50];
+    int sortCount = 0;
+
+    for(int r=0;r<2;r++) {
+      Sort sort;
+      boolean reverse = 1 == r;
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("byte", SortField.BYTE, reverse)});
+      
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("short", SortField.SHORT, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("int", SortField.INT, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("long", SortField.LONG, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("float", SortField.FLOAT, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("double", SortField.DOUBLE, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("string", SortField.STRING_VAL, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField("string", SortField.STRING, reverse)});
+
+      //sorts[sortCount++] = sort = new Sort();
+      //sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD, reverse)});
+
+      //sorts[sortCount++] = sort = new Sort();
+      //sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD_VAL, reverse)});
+
+      //sorts[sortCount++] = sort = new Sort();
+      //sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD_VAL_DEM, reverse)});
+
+      //sorts[sortCount++] = sort = new Sort();
+      //sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD_VAL_DEM2, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField(null, SortField.SCORE, reverse)});
+
+      sorts[sortCount++] = sort = new Sort();
+      sort.setSort(new SortField[] {new SortField(null, SortField.DOC, reverse)});
+    }
+
+    Query[] queries = new Query[4];
+    queries[0] = new MatchAllDocsQuery();
+    queries[1] = new TermQuery(new Term("contents", "x"));  // matches every 10th doc
+    queries[2] = new TermQuery(new Term("contents", "y"));  // matches every 100th doc
+    queries[3] = new TermQuery(new Term("contents", "z"));  // matches every 1000th doc
+
+    for(int sx=0;sx<3;sx++) {
+      final IndexSearcher searcher;
+      if (sx == 0) {
+        searcher = searcherSingleSegment;
+      } else if (sx == 1) {
+        searcher = searcherFewSegment;
+      } else {
+        searcher = searcherMultiSegment;
+      }
+
+      for(int qx=0;qx<queries.length;qx++) {
+        final Query query = queries[qx];
+
+        for(int q=0;q<3;q++) {
+
+          final int queueSize;
+          if (q == 0) {
+            queueSize = 10;
+          } else if (q == 1) {
+            queueSize = 100;
+          } else {
+            queueSize = 1000;
+          }
+        
+          for(int s=0;s<sortCount;s++) {
+            Sort sort1 = sorts[s];
+
+            for(int s2=-1;s2<sortCount;s2++) {
+              Sort sort;
+              if (s2 == -1) {
+                // Single field sort
+                sort = sort1;
+              } else {
+                sort = new Sort(new SortField[] {sort1.getSort()[0], sorts[s2].getSort()[0]});
+              }
+
+              // Old
+              Sort oldSort = getOldSort(sort);
+
+              if (VERBOSE) {
+                System.out.println("query=" + query);
+                if (sx == 0) {
+                  System.out.println("  single-segment index");
+                } else if (sx == 1) {
+                  System.out.println("  few-segment index");
+                } else {
+                  System.out.println("  many-segment index");
+                }
+                System.out.println("  numHit=" + queueSize);
+                System.out.println("  old=" + oldSort);
+                System.out.println("  new=" + sort);
+              }
+
+              TopDocs newHits = searcher.search(query, null, queueSize, sort);
+              TopDocs oldHits = searcher.search(query, null, queueSize, oldSort);
+
+              compare(oldHits, newHits);
+            }
+          }
+        }
+      }
+    }
+
+    close();
+  }
+
+  private Sort getOldSort(Sort sort) {
+    SortField[] fields = sort.getSort();
+    SortField[] oldFields = new SortField[fields.length];
+    for(int i=0;i<fields.length;i++) {
+      int sortType;
+      if (fields[i].getField() != null && fields[i].getField().equals("string")) {
+        sortType = SortField.STRING;
+      } else {
+        sortType = fields[i].getType();
+      }
+      oldFields[i] = new SortField(fields[i].getField(),
+                                   sortType,
+                                   fields[i].getReverse());
+      oldFields[i].setUseLegacySearch(true);
+    }
+    return new Sort(oldFields);
+  }
+
+  private void compare(TopDocs oldHits, TopDocs newHits) {
+    assertEquals(oldHits.totalHits, newHits.totalHits);
+    assertEquals(oldHits.scoreDocs.length, newHits.scoreDocs.length);
+    final ScoreDoc[] oldDocs = oldHits.scoreDocs;
+    final ScoreDoc[] newDocs = newHits.scoreDocs;
+
+    for(int i=0;i<oldDocs.length;i++) {
+      if (oldDocs[i] instanceof FieldDoc) {
+        assert newDocs[i] instanceof FieldDoc;
+        FieldDoc oldHit = (FieldDoc) oldDocs[i];
+        FieldDoc newHit = (FieldDoc) newDocs[i];
+        assertEquals("hit " + i + " of " + oldDocs.length + " differs: oldDoc=" + oldHit.doc + " vs newDoc=" + newHit.doc + " oldFields=" + Arrays.toString(oldHit.fields) + " newFields=" + Arrays.toString(newHit.fields),
+                     oldHit.doc, newHit.doc);
+        assertEquals(oldHit.score, newHit.score, 0.00001);
+        assertTrue(Arrays.equals(oldHit.fields, newHit.fields));
+      } else {
+        ScoreDoc oldHit = oldDocs[i];
+        ScoreDoc newHit = newDocs[i];
+        assertEquals(oldHit.doc, newHit.doc);
+        assertEquals(oldHit.score, newHit.score, 0.00001);
+      }
+    }
+    
+  }
+}

Propchange: lucene/java/trunk/src/test/org/apache/lucene/search/TestStressSort.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestTermScorer.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestTermScorer.java?rev=738219&r1=738218&r2=738219&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestTermScorer.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestTermScorer.java Tue Jan 27 20:15:21 2009
@@ -82,15 +82,19 @@
         //must call next first
 
 
-        ts.score(new HitCollector()
+        ts.score(new MultiReaderHitCollector()
         {
+            private int base = -1;
             public void collect(int doc, float score)
             {
-                docs.add(new TestHit(doc, score));
+                docs.add(new TestHit(doc + base, score));
                 assertTrue("score " + score + " is not greater than 0", score > 0);
                 assertTrue("Doc: " + doc + " does not equal: " + 0 +
                         " or doc does not equaal: " + 5, doc == 0 || doc == 5);
             }
+            public void setNextReader(IndexReader reader, int docBase) {
+              base = docBase;
+            }
         });
         assertTrue("docs Size: " + docs.size() + " is not: " + 2, docs.size() == 2);
         TestHit doc0 = (TestHit) docs.get(0);

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestTimeLimitedCollector.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestTimeLimitedCollector.java?rev=738219&r1=738218&r2=738219&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestTimeLimitedCollector.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestTimeLimitedCollector.java Tue Jan 27 20:15:21 2009
@@ -20,6 +20,7 @@
 import org.apache.lucene.analysis.WhitespaceAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriter.MaxFieldLength;
 import org.apache.lucene.queryParser.QueryParser;
@@ -125,6 +126,7 @@
       search(tlCollector);
       totalTLCResults = myHc.hitCount();
     } catch (Exception e) {
+      e.printStackTrace();
       assertTrue("Unexpected exception: "+e, false); //==fail
     }
     assertEquals( "Wrong number of results!", totalResults, totalTLCResults );
@@ -293,11 +295,12 @@
   }
   
   // counting hit collector that can slow down at collect().
-  private class MyHitCollector extends HitCollector
+  private class MyHitCollector extends MultiReaderHitCollector
   {
     private final BitSet bits = new BitSet();
     private int slowdown = 0;
     private int lastDocCollected = -1;
+    private int docBase = -1;
 
     /**
      * amount of time to wait on each collect to simulate a long iteration
@@ -307,6 +310,7 @@
     }
     
     public void collect( final int doc, final float score ) {
+      int docId = doc + docBase;
       if( slowdown > 0 ) {
         try {
           Thread.sleep(slowdown);
@@ -315,8 +319,9 @@
           System.out.println("caught " + x);
         }
       }
-      bits.set( doc );
-      lastDocCollected = doc;
+      assert docId >= 0: " base=" + docBase + " doc=" + doc;
+      bits.set( docId );
+      lastDocCollected = docId;
     }
     
     public int hitCount() {
@@ -326,6 +331,10 @@
     public int getLastDocCollected() {
       return lastDocCollected;
     }
+
+    public void setNextReader(IndexReader reader, int base) {
+      docBase = base;
+    }
     
   }