pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test-framework / java / org / apache / lucene / util / _TestUtil.java
1 package org.apache.lucene.util;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.BufferedOutputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.io.PrintStream;
28 import java.lang.reflect.Method;
29 import java.util.Enumeration;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.Random;
33 import java.util.zip.ZipEntry;
34 import java.util.zip.ZipFile;
35
36 import org.junit.Assert;
37
38 import org.apache.lucene.document.Document;
39 import org.apache.lucene.document.Field;
40 import org.apache.lucene.document.Fieldable;
41 import org.apache.lucene.index.CheckIndex;
42 import org.apache.lucene.index.ConcurrentMergeScheduler;
43 import org.apache.lucene.index.IndexWriter;
44 import org.apache.lucene.index.LogMergePolicy;
45 import org.apache.lucene.index.MergePolicy;
46 import org.apache.lucene.index.MergeScheduler;
47 import org.apache.lucene.index.TieredMergePolicy;
48 import org.apache.lucene.search.FieldDoc;
49 import org.apache.lucene.search.ScoreDoc;
50 import org.apache.lucene.search.TopDocs;
51 import org.apache.lucene.store.Directory;
52
53 public class _TestUtil {
54
55   /** Returns temp dir, based on String arg in its name;
56    *  does not create the directory. */
57   public static File getTempDir(String desc) {
58     try {
59       File f = createTempFile(desc, "tmp", LuceneTestCase.TEMP_DIR);
60       f.delete();
61       LuceneTestCase.registerTempDir(f);
62       return f;
63     } catch (IOException e) {
64       throw new RuntimeException(e);
65     }
66   }
67
68   /**
69    * Deletes a directory and everything underneath it.
70    */
71   public static void rmDir(File dir) throws IOException {
72     if (dir.exists()) {
73       if (dir.isFile() && !dir.delete()) {
74         throw new IOException("could not delete " + dir);
75       }
76       for (File f : dir.listFiles()) {
77         if (f.isDirectory()) {
78           rmDir(f);
79         } else {
80           if (!f.delete()) {
81             throw new IOException("could not delete " + f);
82           }
83         }
84       }
85       if (!dir.delete()) {
86         throw new IOException("could not delete " + dir);
87       }
88     }
89   }
90
91   /** 
92    * Convenience method: Unzip zipName + ".zip" under destDir, removing destDir first 
93    */
94   public static void unzip(File zipName, File destDir) throws IOException {
95     
96     ZipFile zipFile = new ZipFile(zipName);
97     
98     Enumeration<? extends ZipEntry> entries = zipFile.entries();
99     
100     rmDir(destDir);
101     
102     destDir.mkdir();
103     LuceneTestCase.registerTempDir(destDir);
104     
105     while (entries.hasMoreElements()) {
106       ZipEntry entry = entries.nextElement();
107       
108       InputStream in = zipFile.getInputStream(entry);
109       File targetFile = new File(destDir, entry.getName());
110       if (entry.isDirectory()) {
111         // allow unzipping with directory structure
112         targetFile.mkdirs();
113       } else {
114         if (targetFile.getParentFile()!=null) {
115           // be on the safe side: do not rely on that directories are always extracted
116           // before their children (although this makes sense, but is it guaranteed?)
117           targetFile.getParentFile().mkdirs();   
118         }
119         OutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile));
120         
121         byte[] buffer = new byte[8192];
122         int len;
123         while((len = in.read(buffer)) >= 0) {
124           out.write(buffer, 0, len);
125         }
126         
127         in.close();
128         out.close();
129       }
130     }
131     
132     zipFile.close();
133   }
134   
135   public static void syncConcurrentMerges(IndexWriter writer) {
136     syncConcurrentMerges(writer.getConfig().getMergeScheduler());
137   }
138
139   public static void syncConcurrentMerges(MergeScheduler ms) {
140     if (ms instanceof ConcurrentMergeScheduler)
141       ((ConcurrentMergeScheduler) ms).sync();
142   }
143
144   /** This runs the CheckIndex tool on the index in.  If any
145    *  issues are hit, a RuntimeException is thrown; else,
146    *  true is returned. */
147   public static CheckIndex.Status checkIndex(Directory dir) throws IOException {
148     ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
149
150     CheckIndex checker = new CheckIndex(dir);
151     checker.setInfoStream(new PrintStream(bos));
152     CheckIndex.Status indexStatus = checker.checkIndex();
153     if (indexStatus == null || indexStatus.clean == false) {
154       System.out.println("CheckIndex failed");
155       System.out.println(bos.toString());
156       throw new RuntimeException("CheckIndex failed");
157     } else {
158       return indexStatus;
159     }
160   }
161
162   /** Use only for testing.
163    *  @deprecated -- in 3.0 we can use Arrays.toString
164    *  instead */
165   @Deprecated
166   public static String arrayToString(int[] array) {
167     StringBuilder buf = new StringBuilder();
168     buf.append("[");
169     for(int i=0;i<array.length;i++) {
170       if (i > 0) {
171         buf.append(" ");
172       }
173       buf.append(array[i]);
174     }
175     buf.append("]");
176     return buf.toString();
177   }
178
179   /** start and end are BOTH inclusive */
180   public static int nextInt(Random r, int start, int end) {
181     return start + r.nextInt(end-start+1);
182   }
183
184   // NOTE: only works for TMP and LMP!!
185   public static void setUseCompoundFile(MergePolicy mp, boolean v) {
186     if (mp instanceof TieredMergePolicy) {
187       ((TieredMergePolicy) mp).setUseCompoundFile(v);
188     } else if (mp instanceof LogMergePolicy) {
189       ((LogMergePolicy) mp).setUseCompoundFile(v);
190     }
191   }
192
193   /** Use only for testing.
194    *  @deprecated -- in 3.0 we can use Arrays.toString
195    *  instead */
196   @Deprecated
197   public static String arrayToString(Object[] array) {
198     StringBuilder buf = new StringBuilder();
199     buf.append("[");
200     for(int i=0;i<array.length;i++) {
201       if (i > 0) {
202         buf.append(" ");
203       }
204       buf.append(array[i]);
205     }
206     buf.append("]");
207     return buf.toString();
208   }
209
210   public static String randomSimpleString(Random r) {
211     final int end = r.nextInt(10);
212     if (end == 0) {
213       // allow 0 length
214       return "";
215     }
216     final char[] buffer = new char[end];
217     for (int i = 0; i < end; i++) {
218       buffer[i] = (char) _TestUtil.nextInt(r, 97, 102);
219     }
220     return new String(buffer, 0, end);
221   }
222
223   /** Returns random string, including full unicode range. */
224   public static String randomUnicodeString(Random r) {
225     return randomUnicodeString(r, 20);
226   }
227
228   /**
229    * Returns a random string up to a certain length.
230    */
231   public static String randomUnicodeString(Random r, int maxLength) {
232     final int end = r.nextInt(maxLength);
233     if (end == 0) {
234       // allow 0 length
235       return "";
236     }
237     final char[] buffer = new char[end];
238     randomFixedLengthUnicodeString(r, buffer, 0, buffer.length);
239     return new String(buffer, 0, end);
240   }
241
242   /**
243    * Fills provided char[] with valid random unicode code
244    * unit sequence.
245    */
246   public static void randomFixedLengthUnicodeString(Random random, char[] chars, int offset, int length) {
247     int i = offset;
248     final int end = offset + length;
249     while(i < end) {
250       final int t = random.nextInt(5);
251       if (0 == t && i < length - 1) {
252         // Make a surrogate pair
253         // High surrogate
254         chars[i++] = (char) nextInt(random, 0xd800, 0xdbff);
255         // Low surrogate
256         chars[i++] = (char) nextInt(random, 0xdc00, 0xdfff);
257       } else if (t <= 1) {
258         chars[i++] = (char) random.nextInt(0x80);
259       } else if (2 == t) {
260         chars[i++] = (char) nextInt(random, 0x80, 0x7ff);
261       } else if (3 == t) {
262         chars[i++] = (char) nextInt(random, 0x800, 0xd7ff);
263       } else if (4 == t) {
264         chars[i++] = (char) nextInt(random, 0xe000, 0xfffe);
265       }
266     }
267   }
268
269   private static final int[] blockStarts = {
270     0x0000, 0x0080, 0x0100, 0x0180, 0x0250, 0x02B0, 0x0300, 0x0370, 0x0400, 
271     0x0500, 0x0530, 0x0590, 0x0600, 0x0700, 0x0750, 0x0780, 0x07C0, 0x0800, 
272     0x0900, 0x0980, 0x0A00, 0x0A80, 0x0B00, 0x0B80, 0x0C00, 0x0C80, 0x0D00, 
273     0x0D80, 0x0E00, 0x0E80, 0x0F00, 0x1000, 0x10A0, 0x1100, 0x1200, 0x1380, 
274     0x13A0, 0x1400, 0x1680, 0x16A0, 0x1700, 0x1720, 0x1740, 0x1760, 0x1780, 
275     0x1800, 0x18B0, 0x1900, 0x1950, 0x1980, 0x19E0, 0x1A00, 0x1A20, 0x1B00, 
276     0x1B80, 0x1C00, 0x1C50, 0x1CD0, 0x1D00, 0x1D80, 0x1DC0, 0x1E00, 0x1F00, 
277     0x2000, 0x2070, 0x20A0, 0x20D0, 0x2100, 0x2150, 0x2190, 0x2200, 0x2300, 
278     0x2400, 0x2440, 0x2460, 0x2500, 0x2580, 0x25A0, 0x2600, 0x2700, 0x27C0, 
279     0x27F0, 0x2800, 0x2900, 0x2980, 0x2A00, 0x2B00, 0x2C00, 0x2C60, 0x2C80, 
280     0x2D00, 0x2D30, 0x2D80, 0x2DE0, 0x2E00, 0x2E80, 0x2F00, 0x2FF0, 0x3000, 
281     0x3040, 0x30A0, 0x3100, 0x3130, 0x3190, 0x31A0, 0x31C0, 0x31F0, 0x3200, 
282     0x3300, 0x3400, 0x4DC0, 0x4E00, 0xA000, 0xA490, 0xA4D0, 0xA500, 0xA640, 
283     0xA6A0, 0xA700, 0xA720, 0xA800, 0xA830, 0xA840, 0xA880, 0xA8E0, 0xA900, 
284     0xA930, 0xA960, 0xA980, 0xAA00, 0xAA60, 0xAA80, 0xABC0, 0xAC00, 0xD7B0, 
285     0xE000, 0xF900, 0xFB00, 0xFB50, 0xFE00, 0xFE10, 
286     0xFE20, 0xFE30, 0xFE50, 0xFE70, 0xFF00, 0xFFF0, 
287     0x10000, 0x10080, 0x10100, 0x10140, 0x10190, 0x101D0, 0x10280, 0x102A0, 
288     0x10300, 0x10330, 0x10380, 0x103A0, 0x10400, 0x10450, 0x10480, 0x10800, 
289     0x10840, 0x10900, 0x10920, 0x10A00, 0x10A60, 0x10B00, 0x10B40, 0x10B60, 
290     0x10C00, 0x10E60, 0x11080, 0x12000, 0x12400, 0x13000, 0x1D000, 0x1D100, 
291     0x1D200, 0x1D300, 0x1D360, 0x1D400, 0x1F000, 0x1F030, 0x1F100, 0x1F200, 
292     0x20000, 0x2A700, 0x2F800, 0xE0000, 0xE0100, 0xF0000, 0x100000
293   };
294   
295   private static final int[] blockEnds = {
296     0x007F, 0x00FF, 0x017F, 0x024F, 0x02AF, 0x02FF, 0x036F, 0x03FF, 0x04FF, 
297     0x052F, 0x058F, 0x05FF, 0x06FF, 0x074F, 0x077F, 0x07BF, 0x07FF, 0x083F, 
298     0x097F, 0x09FF, 0x0A7F, 0x0AFF, 0x0B7F, 0x0BFF, 0x0C7F, 0x0CFF, 0x0D7F, 
299     0x0DFF, 0x0E7F, 0x0EFF, 0x0FFF, 0x109F, 0x10FF, 0x11FF, 0x137F, 0x139F, 
300     0x13FF, 0x167F, 0x169F, 0x16FF, 0x171F, 0x173F, 0x175F, 0x177F, 0x17FF, 
301     0x18AF, 0x18FF, 0x194F, 0x197F, 0x19DF, 0x19FF, 0x1A1F, 0x1AAF, 0x1B7F, 
302     0x1BBF, 0x1C4F, 0x1C7F, 0x1CFF, 0x1D7F, 0x1DBF, 0x1DFF, 0x1EFF, 0x1FFF, 
303     0x206F, 0x209F, 0x20CF, 0x20FF, 0x214F, 0x218F, 0x21FF, 0x22FF, 0x23FF, 
304     0x243F, 0x245F, 0x24FF, 0x257F, 0x259F, 0x25FF, 0x26FF, 0x27BF, 0x27EF, 
305     0x27FF, 0x28FF, 0x297F, 0x29FF, 0x2AFF, 0x2BFF, 0x2C5F, 0x2C7F, 0x2CFF, 
306     0x2D2F, 0x2D7F, 0x2DDF, 0x2DFF, 0x2E7F, 0x2EFF, 0x2FDF, 0x2FFF, 0x303F, 
307     0x309F, 0x30FF, 0x312F, 0x318F, 0x319F, 0x31BF, 0x31EF, 0x31FF, 0x32FF, 
308     0x33FF, 0x4DBF, 0x4DFF, 0x9FFF, 0xA48F, 0xA4CF, 0xA4FF, 0xA63F, 0xA69F, 
309     0xA6FF, 0xA71F, 0xA7FF, 0xA82F, 0xA83F, 0xA87F, 0xA8DF, 0xA8FF, 0xA92F, 
310     0xA95F, 0xA97F, 0xA9DF, 0xAA5F, 0xAA7F, 0xAADF, 0xABFF, 0xD7AF, 0xD7FF, 
311     0xF8FF, 0xFAFF, 0xFB4F, 0xFDFF, 0xFE0F, 0xFE1F, 
312     0xFE2F, 0xFE4F, 0xFE6F, 0xFEFF, 0xFFEF, 0xFFFE, /* avoid 0xFFFF on 3.x */
313     0x1007F, 0x100FF, 0x1013F, 0x1018F, 0x101CF, 0x101FF, 0x1029F, 0x102DF, 
314     0x1032F, 0x1034F, 0x1039F, 0x103DF, 0x1044F, 0x1047F, 0x104AF, 0x1083F, 
315     0x1085F, 0x1091F, 0x1093F, 0x10A5F, 0x10A7F, 0x10B3F, 0x10B5F, 0x10B7F, 
316     0x10C4F, 0x10E7F, 0x110CF, 0x123FF, 0x1247F, 0x1342F, 0x1D0FF, 0x1D1FF, 
317     0x1D24F, 0x1D35F, 0x1D37F, 0x1D7FF, 0x1F02F, 0x1F09F, 0x1F1FF, 0x1F2FF, 
318     0x2A6DF, 0x2B73F, 0x2FA1F, 0xE007F, 0xE01EF, 0xFFFFF, 0x10FFFF
319   };
320   
321   /** Returns random string of length between 0-20 codepoints, all codepoints within the same unicode block. */
322   public static String randomRealisticUnicodeString(Random r) {
323     return randomRealisticUnicodeString(r, 20);
324   }
325   
326   /** Returns random string of length up to maxLength codepoints , all codepoints within the same unicode block. */
327   public static String randomRealisticUnicodeString(Random r, int maxLength) {
328     return randomRealisticUnicodeString(r, 0, 20);
329   }
330
331   /** Returns random string of length between min and max codepoints, all codepoints within the same unicode block. */
332   public static String randomRealisticUnicodeString(Random r, int minLength, int maxLength) {
333     final int end = minLength + r.nextInt(maxLength);
334     final int block = r.nextInt(blockStarts.length);
335     StringBuilder sb = new StringBuilder();
336     for (int i = 0; i < end; i++)
337       sb.appendCodePoint(nextInt(r, blockStarts[block], blockEnds[block]));
338     return sb.toString();
339   }
340
341   /** Returns random string, with a given UTF-8 byte length*/
342   public static String randomFixedByteLengthUnicodeString(Random r, int length) {
343     
344     final char[] buffer = new char[length*3];
345     int bytes = length;
346     int i = 0;
347     for (; i < buffer.length && bytes != 0; i++) {
348       int t;
349       if (bytes >= 4) {
350         t = r.nextInt(5);
351       } else if (bytes >= 3) {
352         t = r.nextInt(4);
353       } else if (bytes >= 2) {
354         t = r.nextInt(2);
355       } else {
356         t = 0;
357       }
358       if (t == 0) {
359         buffer[i] = (char) r.nextInt(0x80);
360         bytes--;
361       } else if (1 == t) {
362         buffer[i] = (char) nextInt(r, 0x80, 0x7ff);
363         bytes -= 2;
364       } else if (2 == t) {
365         buffer[i] = (char) nextInt(r, 0x800, 0xd7ff);
366         bytes -= 3;
367       } else if (3 == t) {
368         buffer[i] = (char) nextInt(r, 0xe000, 0xfffe);
369         bytes -= 3;
370       } else if (4 == t) {
371         // Make a surrogate pair
372         // High surrogate
373         buffer[i++] = (char) nextInt(r, 0xd800, 0xdbff);
374         // Low surrogate
375         buffer[i] = (char) nextInt(r, 0xdc00, 0xdfff);
376         bytes -= 4;
377       }
378
379     }
380     return new String(buffer, 0, i);
381   }
382
383   public static boolean anyFilesExceptWriteLock(Directory dir) throws IOException {
384     String[] files = dir.listAll();
385     if (files.length > 1 || (files.length == 1 && !files[0].equals("write.lock"))) {
386       return true;
387     } else {
388       return false;
389     }
390   }
391
392   /** just tries to configure things to keep the open file
393    * count lowish */
394   public static void reduceOpenFiles(IndexWriter w) {
395     // keep number of open files lowish
396     MergePolicy mp = w.getConfig().getMergePolicy();
397     if (mp instanceof LogMergePolicy) {
398       LogMergePolicy lmp = (LogMergePolicy) mp;
399       lmp.setMergeFactor(Math.min(5, lmp.getMergeFactor()));
400     } else if (mp instanceof TieredMergePolicy) {
401       TieredMergePolicy tmp = (TieredMergePolicy) mp;
402       tmp.setMaxMergeAtOnce(Math.min(5, tmp.getMaxMergeAtOnce()));
403       tmp.setSegmentsPerTier(Math.min(5, tmp.getSegmentsPerTier()));
404     }
405
406     MergeScheduler ms = w.getConfig().getMergeScheduler();
407     if (ms instanceof ConcurrentMergeScheduler) {
408       ((ConcurrentMergeScheduler) ms).setMaxThreadCount(2);
409       ((ConcurrentMergeScheduler) ms).setMaxMergeCount(3);
410     }
411   }
412
413   /** Checks some basic behaviour of an AttributeImpl
414    * @param reflectedValues contains a map with "AttributeClass#key" as values
415    */
416   public static <T> void assertAttributeReflection(final AttributeImpl att, Map<String,T> reflectedValues) {
417     final Map<String,Object> map = new HashMap<String,Object>();
418     att.reflectWith(new AttributeReflector() {
419       public void reflect(Class<? extends Attribute> attClass, String key, Object value) {
420         map.put(attClass.getName() + '#' + key, value);
421       }
422     });
423     Assert.assertEquals("Reflection does not produce same map", reflectedValues, map);
424   }
425
426   public static void keepFullyDeletedSegments(IndexWriter w) {
427     try {
428       // Carefully invoke what is a package-private (test
429       // only, internal) method on IndexWriter:
430       Method m = IndexWriter.class.getDeclaredMethod("keepFullyDeletedSegments");
431       m.setAccessible(true);
432       m.invoke(w);
433     } catch (Exception e) {
434       // Should not happen?
435       throw new RuntimeException(e);
436     }
437   }
438   
439   /** 
440    * insecure, fast version of File.createTempFile
441    * uses Random instead of SecureRandom.
442    */
443   public static File createTempFile(String prefix, String suffix, File directory)
444       throws IOException {
445     // Force a prefix null check first
446     if (prefix.length() < 3) {
447       throw new IllegalArgumentException("prefix must be 3");
448     }
449     String newSuffix = suffix == null ? ".tmp" : suffix;
450     File result;
451     do {
452       result = genTempFile(prefix, newSuffix, directory);
453     } while (!result.createNewFile());
454     return result;
455   }
456
457   /* Temp file counter */
458   private static int counter = 0;
459
460   /* identify for differnt VM processes */
461   private static int counterBase = 0;
462
463   private static class TempFileLocker {};
464   private static TempFileLocker tempFileLocker = new TempFileLocker();
465
466   private static File genTempFile(String prefix, String suffix, File directory) {
467     int identify = 0;
468
469     synchronized (tempFileLocker) {
470       if (counter == 0) {
471         int newInt = new Random().nextInt();
472         counter = ((newInt / 65535) & 0xFFFF) + 0x2710;
473         counterBase = counter;
474       }
475       identify = counter++;
476     }
477
478     StringBuilder newName = new StringBuilder();
479     newName.append(prefix);
480     newName.append(counterBase);
481     newName.append(identify);
482     newName.append(suffix);
483     return new File(directory, newName.toString());
484   }
485
486   public static void assertEquals(TopDocs expected, TopDocs actual) {
487     Assert.assertEquals("wrong total hits", expected.totalHits, actual.totalHits);
488     Assert.assertEquals("wrong maxScore", expected.getMaxScore(), actual.getMaxScore(), 0.0);
489     Assert.assertEquals("wrong hit count", expected.scoreDocs.length, actual.scoreDocs.length);
490     for(int hitIDX=0;hitIDX<expected.scoreDocs.length;hitIDX++) {
491       final ScoreDoc expectedSD = expected.scoreDocs[hitIDX];
492       final ScoreDoc actualSD = actual.scoreDocs[hitIDX];
493       Assert.assertEquals("wrong hit docID", expectedSD.doc, actualSD.doc);
494       Assert.assertEquals("wrong hit score", expectedSD.score, actualSD.score, 0.0);
495       if (expectedSD instanceof FieldDoc) {
496         Assert.assertTrue(actualSD instanceof FieldDoc);
497         Assert.assertArrayEquals("wrong sort field values",
498                             ((FieldDoc) expectedSD).fields,
499                             ((FieldDoc) actualSD).fields);
500       } else {
501         Assert.assertFalse(actualSD instanceof FieldDoc);
502       }
503     }
504   }
505
506   // NOTE: this is likely buggy, and cannot clone fields
507   // with tokenStreamValues, etc.  Use at your own risk!!
508
509   // TODO: is there a pre-existing way to do this!!!
510   public static Document cloneDocument(Document doc1) {
511     final Document doc2 = new Document();
512     for(Fieldable f : doc1.getFields()) {
513       Field field1 = (Field) f;
514       
515       Field field2 = new Field(field1.name(),
516                                field1.stringValue(),
517                                field1.isStored() ? Field.Store.YES : Field.Store.NO,
518                                field1.isIndexed() ? (field1.isTokenized() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED) : Field.Index.NO);
519       field2.setOmitNorms(field1.getOmitNorms());
520       field2.setIndexOptions(field1.getIndexOptions());
521       doc2.add(field2);
522     }
523
524     return doc2;
525   }
526 }