1 package org.apache.lucene.util;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 import java.io.BufferedOutputStream;
21 import java.io.ByteArrayOutputStream;
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;
32 import java.util.Random;
33 import java.util.zip.ZipEntry;
34 import java.util.zip.ZipFile;
36 import org.junit.Assert;
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;
53 public class _TestUtil {
55 /** Returns temp dir, based on String arg in its name;
56 * does not create the directory. */
57 public static File getTempDir(String desc) {
59 File f = createTempFile(desc, "tmp", LuceneTestCase.TEMP_DIR);
61 LuceneTestCase.registerTempDir(f);
63 } catch (IOException e) {
64 throw new RuntimeException(e);
69 * Deletes a directory and everything underneath it.
71 public static void rmDir(File dir) throws IOException {
73 if (dir.isFile() && !dir.delete()) {
74 throw new IOException("could not delete " + dir);
76 for (File f : dir.listFiles()) {
77 if (f.isDirectory()) {
81 throw new IOException("could not delete " + f);
86 throw new IOException("could not delete " + dir);
92 * Convenience method: Unzip zipName + ".zip" under destDir, removing destDir first
94 public static void unzip(File zipName, File destDir) throws IOException {
96 ZipFile zipFile = new ZipFile(zipName);
98 Enumeration<? extends ZipEntry> entries = zipFile.entries();
103 LuceneTestCase.registerTempDir(destDir);
105 while (entries.hasMoreElements()) {
106 ZipEntry entry = entries.nextElement();
108 InputStream in = zipFile.getInputStream(entry);
109 File targetFile = new File(destDir, entry.getName());
110 if (entry.isDirectory()) {
111 // allow unzipping with directory structure
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();
119 OutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile));
121 byte[] buffer = new byte[8192];
123 while((len = in.read(buffer)) >= 0) {
124 out.write(buffer, 0, len);
135 public static void syncConcurrentMerges(IndexWriter writer) {
136 syncConcurrentMerges(writer.getConfig().getMergeScheduler());
139 public static void syncConcurrentMerges(MergeScheduler ms) {
140 if (ms instanceof ConcurrentMergeScheduler)
141 ((ConcurrentMergeScheduler) ms).sync();
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);
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");
162 /** Use only for testing.
163 * @deprecated -- in 3.0 we can use Arrays.toString
166 public static String arrayToString(int[] array) {
167 StringBuilder buf = new StringBuilder();
169 for(int i=0;i<array.length;i++) {
173 buf.append(array[i]);
176 return buf.toString();
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);
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);
193 /** Use only for testing.
194 * @deprecated -- in 3.0 we can use Arrays.toString
197 public static String arrayToString(Object[] array) {
198 StringBuilder buf = new StringBuilder();
200 for(int i=0;i<array.length;i++) {
204 buf.append(array[i]);
207 return buf.toString();
210 public static String randomSimpleString(Random r) {
211 final int end = r.nextInt(10);
216 final char[] buffer = new char[end];
217 for (int i = 0; i < end; i++) {
218 buffer[i] = (char) _TestUtil.nextInt(r, 97, 102);
220 return new String(buffer, 0, end);
223 /** Returns random string, including full unicode range. */
224 public static String randomUnicodeString(Random r) {
225 return randomUnicodeString(r, 20);
229 * Returns a random string up to a certain length.
231 public static String randomUnicodeString(Random r, int maxLength) {
232 final int end = r.nextInt(maxLength);
237 final char[] buffer = new char[end];
238 randomFixedLengthUnicodeString(r, buffer, 0, buffer.length);
239 return new String(buffer, 0, end);
243 * Fills provided char[] with valid random unicode code
246 public static void randomFixedLengthUnicodeString(Random random, char[] chars, int offset, int length) {
248 final int end = offset + length;
250 final int t = random.nextInt(5);
251 if (0 == t && i < length - 1) {
252 // Make a surrogate pair
254 chars[i++] = (char) nextInt(random, 0xd800, 0xdbff);
256 chars[i++] = (char) nextInt(random, 0xdc00, 0xdfff);
258 chars[i++] = (char) random.nextInt(0x80);
260 chars[i++] = (char) nextInt(random, 0x80, 0x7ff);
262 chars[i++] = (char) nextInt(random, 0x800, 0xd7ff);
264 chars[i++] = (char) nextInt(random, 0xe000, 0xfffe);
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
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
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);
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);
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();
341 /** Returns random string, with a given UTF-8 byte length*/
342 public static String randomFixedByteLengthUnicodeString(Random r, int length) {
344 final char[] buffer = new char[length*3];
347 for (; i < buffer.length && bytes != 0; i++) {
351 } else if (bytes >= 3) {
353 } else if (bytes >= 2) {
359 buffer[i] = (char) r.nextInt(0x80);
362 buffer[i] = (char) nextInt(r, 0x80, 0x7ff);
365 buffer[i] = (char) nextInt(r, 0x800, 0xd7ff);
368 buffer[i] = (char) nextInt(r, 0xe000, 0xfffe);
371 // Make a surrogate pair
373 buffer[i++] = (char) nextInt(r, 0xd800, 0xdbff);
375 buffer[i] = (char) nextInt(r, 0xdc00, 0xdfff);
380 return new String(buffer, 0, i);
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"))) {
392 /** just tries to configure things to keep the open file
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()));
406 MergeScheduler ms = w.getConfig().getMergeScheduler();
407 if (ms instanceof ConcurrentMergeScheduler) {
408 ((ConcurrentMergeScheduler) ms).setMaxThreadCount(2);
409 ((ConcurrentMergeScheduler) ms).setMaxMergeCount(3);
413 /** Checks some basic behaviour of an AttributeImpl
414 * @param reflectedValues contains a map with "AttributeClass#key" as values
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);
423 Assert.assertEquals("Reflection does not produce same map", reflectedValues, map);
426 public static void keepFullyDeletedSegments(IndexWriter w) {
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);
433 } catch (Exception e) {
434 // Should not happen?
435 throw new RuntimeException(e);
440 * insecure, fast version of File.createTempFile
441 * uses Random instead of SecureRandom.
443 public static File createTempFile(String prefix, String suffix, File directory)
445 // Force a prefix null check first
446 if (prefix.length() < 3) {
447 throw new IllegalArgumentException("prefix must be 3");
449 String newSuffix = suffix == null ? ".tmp" : suffix;
452 result = genTempFile(prefix, newSuffix, directory);
453 } while (!result.createNewFile());
457 /* Temp file counter */
458 private static int counter = 0;
460 /* identify for differnt VM processes */
461 private static int counterBase = 0;
463 private static class TempFileLocker {};
464 private static TempFileLocker tempFileLocker = new TempFileLocker();
466 private static File genTempFile(String prefix, String suffix, File directory) {
469 synchronized (tempFileLocker) {
471 int newInt = new Random().nextInt();
472 counter = ((newInt / 65535) & 0xFFFF) + 0x2710;
473 counterBase = counter;
475 identify = counter++;
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());
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);
501 Assert.assertFalse(actualSD instanceof FieldDoc);
506 // NOTE: this is likely buggy, and cannot clone fields
507 // with tokenStreamValues, etc. Use at your own risk!!
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;
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());