pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test / org / apache / lucene / store / TestBufferedIndexInput.java
1 package org.apache.lucene.store;
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.File;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Random;
27
28 import org.apache.lucene.analysis.MockAnalyzer;
29 import org.apache.lucene.document.Document;
30 import org.apache.lucene.document.Field;
31 import org.apache.lucene.index.IndexReader;
32 import org.apache.lucene.index.IndexWriter;
33 import org.apache.lucene.index.IndexWriterConfig;
34 import org.apache.lucene.index.Term;
35 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
36 import org.apache.lucene.search.IndexSearcher;
37 import org.apache.lucene.search.ScoreDoc;
38 import org.apache.lucene.search.TermQuery;
39 import org.apache.lucene.store.NIOFSDirectory.NIOFSIndexInput;
40 import org.apache.lucene.store.SimpleFSDirectory.SimpleFSIndexInput;
41 import org.apache.lucene.util.LuceneTestCase;
42 import org.apache.lucene.util._TestUtil;
43 import org.apache.lucene.util.ArrayUtil;
44
45 public class TestBufferedIndexInput extends LuceneTestCase {
46   
47   private static void writeBytes(File aFile, long size) throws IOException{
48     OutputStream stream = null;
49     try {
50       stream = new FileOutputStream(aFile);
51       for (int i = 0; i < size; i++) {
52         stream.write(byten(i));  
53       }
54       stream.flush();
55     } finally {
56       if (stream != null) {
57         stream.close();
58       }
59     }
60   }
61
62   private static final long TEST_FILE_LENGTH = 100*1024;
63  
64   // Call readByte() repeatedly, past the buffer boundary, and see that it
65   // is working as expected.
66   // Our input comes from a dynamically generated/ "file" - see
67   // MyBufferedIndexInput below.
68   public void testReadByte() throws Exception {
69     MyBufferedIndexInput input = new MyBufferedIndexInput();
70     for (int i = 0; i < BufferedIndexInput.BUFFER_SIZE * 10; i++) {
71       assertEquals(input.readByte(), byten(i));
72     }
73   }
74  
75   // Call readBytes() repeatedly, with various chunk sizes (from 1 byte to
76   // larger than the buffer size), and see that it returns the bytes we expect.
77   // Our input comes from a dynamically generated "file" -
78   // see MyBufferedIndexInput below.
79   public void testReadBytes() throws Exception {
80     MyBufferedIndexInput input = new MyBufferedIndexInput();
81     runReadBytes(input, BufferedIndexInput.BUFFER_SIZE, random);
82
83     // This tests the workaround code for LUCENE-1566 where readBytesInternal
84     // provides a workaround for a JVM Bug that incorrectly raises a OOM Error
85     // when a large byte buffer is passed to a file read.
86     // NOTE: this does only test the chunked reads and NOT if the Bug is triggered.
87     //final int tmpFileSize = 1024 * 1024 * 5;
88     final int inputBufferSize = 128;
89     File tmpInputFile = _TestUtil.createTempFile("IndexInput", "tmpFile", TEMP_DIR);
90     tmpInputFile.deleteOnExit();
91     writeBytes(tmpInputFile, TEST_FILE_LENGTH);
92
93     // run test with chunk size of 10 bytes
94     runReadBytesAndClose(new SimpleFSIndexInput("SimpleFSIndexInput(path=\"" + tmpInputFile + "\")", tmpInputFile,
95                                                 inputBufferSize, 10), inputBufferSize, random);
96
97     // run test with chunk size of 10 bytes
98     runReadBytesAndClose(new NIOFSIndexInput(tmpInputFile,
99                                              inputBufferSize, 10), inputBufferSize, random);
100   }
101
102   private void runReadBytesAndClose(IndexInput input, int bufferSize, Random r)
103       throws IOException {
104     try {
105       runReadBytes(input, bufferSize, r);
106     } finally {
107       input.close();
108     }
109   }
110   
111   private void runReadBytes(IndexInput input, int bufferSize, Random r)
112       throws IOException {
113
114     int pos = 0;
115     // gradually increasing size:
116     for (int size = 1; size < bufferSize * 10; size = size + size / 200 + 1) {
117       checkReadBytes(input, size, pos);
118       pos += size;
119       if (pos >= TEST_FILE_LENGTH) {
120         // wrap
121         pos = 0;
122         input.seek(0L);
123       }
124     }
125     // wildly fluctuating size:
126     for (long i = 0; i < 100; i++) {
127       final int size = r.nextInt(10000);
128       checkReadBytes(input, 1+size, pos);
129       pos += 1+size;
130       if (pos >= TEST_FILE_LENGTH) {
131         // wrap
132         pos = 0;
133         input.seek(0L);
134       }
135     }
136     // constant small size (7 bytes):
137     for (int i = 0; i < bufferSize; i++) {
138       checkReadBytes(input, 7, pos);
139       pos += 7;
140       if (pos >= TEST_FILE_LENGTH) {
141         // wrap
142         pos = 0;
143         input.seek(0L);
144       }
145     }
146   }
147
148   private byte[] buffer = new byte[10];
149     
150   private void checkReadBytes(IndexInput input, int size, int pos) throws IOException{
151     // Just to see that "offset" is treated properly in readBytes(), we
152     // add an arbitrary offset at the beginning of the array
153     int offset = size % 10; // arbitrary
154     buffer = ArrayUtil.grow(buffer, offset+size);
155     assertEquals(pos, input.getFilePointer());
156     long left = TEST_FILE_LENGTH - input.getFilePointer();
157     if (left <= 0) {
158       return;
159     } else if (left < size) {
160       size = (int) left;
161     }
162     input.readBytes(buffer, offset, size);
163     assertEquals(pos+size, input.getFilePointer());
164     for(int i=0; i<size; i++) {
165       assertEquals("pos=" + i + " filepos=" + (pos+i), byten(pos+i), buffer[offset+i]);
166     }
167   }
168    
169   // This tests that attempts to readBytes() past an EOF will fail, while
170   // reads up to the EOF will succeed. The EOF is determined by the
171   // BufferedIndexInput's arbitrary length() value.
172   public void testEOF() throws Exception {
173      MyBufferedIndexInput input = new MyBufferedIndexInput(1024);
174      // see that we can read all the bytes at one go:
175      checkReadBytes(input, (int)input.length(), 0);  
176      // go back and see that we can't read more than that, for small and
177      // large overflows:
178      int pos = (int)input.length()-10;
179      input.seek(pos);
180      checkReadBytes(input, 10, pos);  
181      input.seek(pos);
182      try {
183        checkReadBytes(input, 11, pos);
184            fail("Block read past end of file");
185        } catch (IOException e) {
186            /* success */
187        }
188      input.seek(pos);
189      try {
190        checkReadBytes(input, 50, pos);
191            fail("Block read past end of file");
192        } catch (IOException e) {
193            /* success */
194        }
195      input.seek(pos);
196      try {
197        checkReadBytes(input, 100000, pos);
198            fail("Block read past end of file");
199        } catch (IOException e) {
200            /* success */
201        }
202   }
203
204     // byten emulates a file - byten(n) returns the n'th byte in that file.
205     // MyBufferedIndexInput reads this "file".
206     private static byte byten(long n){
207       return (byte)(n*n%256);
208     }
209     private static class MyBufferedIndexInput extends BufferedIndexInput {
210       private long pos;
211       private long len;
212       public MyBufferedIndexInput(long len){
213         super("MyBufferedIndexInput(len=" + len + ")", BufferedIndexInput.BUFFER_SIZE);
214         this.len = len;
215         this.pos = 0;
216       }
217       public MyBufferedIndexInput(){
218         // an infinite file
219         this(Long.MAX_VALUE);
220       }
221       @Override
222       protected void readInternal(byte[] b, int offset, int length) throws IOException {
223         for(int i=offset; i<offset+length; i++)
224           b[i] = byten(pos++);
225       }
226
227       @Override
228       protected void seekInternal(long pos) throws IOException {
229         this.pos = pos;
230       }
231
232       @Override
233       public void close() throws IOException {
234       }
235
236       @Override
237       public long length() {
238         return len;
239       }
240     }
241
242     public void testSetBufferSize() throws IOException {
243       File indexDir = _TestUtil.getTempDir("testSetBufferSize");
244       MockFSDirectory dir = new MockFSDirectory(indexDir, random);
245       try {
246         IndexWriter writer = new IndexWriter(
247             dir,
248             new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).
249                 setOpenMode(OpenMode.CREATE).
250                 setMergePolicy(newLogMergePolicy(false))
251         );
252         for(int i=0;i<37;i++) {
253           Document doc = new Document();
254           doc.add(newField("content", "aaa bbb ccc ddd" + i, Field.Store.YES, Field.Index.ANALYZED));
255           doc.add(newField("id", "" + i, Field.Store.YES, Field.Index.ANALYZED));
256           writer.addDocument(doc);
257         }
258         writer.close();
259
260         dir.allIndexInputs.clear();
261
262         IndexReader reader = IndexReader.open(dir, false);
263         Term aaa = new Term("content", "aaa");
264         Term bbb = new Term("content", "bbb");
265         Term ccc = new Term("content", "ccc");
266         assertEquals(37, reader.docFreq(ccc));
267         reader.deleteDocument(0);
268         assertEquals(37, reader.docFreq(aaa));
269         dir.tweakBufferSizes();
270         reader.deleteDocument(4);
271         assertEquals(reader.docFreq(bbb), 37);
272         dir.tweakBufferSizes();
273
274         IndexSearcher searcher = newSearcher(reader);
275         ScoreDoc[] hits = searcher.search(new TermQuery(bbb), null, 1000).scoreDocs;
276         dir.tweakBufferSizes();
277         assertEquals(35, hits.length);
278         dir.tweakBufferSizes();
279         hits = searcher.search(new TermQuery(new Term("id", "33")), null, 1000).scoreDocs;
280         dir.tweakBufferSizes();
281         assertEquals(1, hits.length);
282         hits = searcher.search(new TermQuery(aaa), null, 1000).scoreDocs;
283         dir.tweakBufferSizes();
284         assertEquals(35, hits.length);
285         searcher.close();
286         reader.close();
287       } finally {
288         _TestUtil.rmDir(indexDir);
289       }
290     }
291
292     private static class MockFSDirectory extends Directory {
293
294       List<IndexInput> allIndexInputs = new ArrayList<IndexInput>();
295
296       Random rand;
297
298       private Directory dir;
299
300       public MockFSDirectory(File path, Random rand) throws IOException {
301         this.rand = rand;
302         lockFactory = NoLockFactory.getNoLockFactory();
303         dir = new SimpleFSDirectory(path, null);
304       }
305
306       @Override
307       public IndexInput openInput(String name) throws IOException {
308         return openInput(name, BufferedIndexInput.BUFFER_SIZE);
309       }
310
311       public void tweakBufferSizes() {
312         //int count = 0;
313         for (final IndexInput ip : allIndexInputs) {
314           BufferedIndexInput bii = (BufferedIndexInput) ip;
315           int bufferSize = 1024+Math.abs(rand.nextInt() % 32768);
316           bii.setBufferSize(bufferSize);
317           //count++;
318         }
319         //System.out.println("tweak'd " + count + " buffer sizes");
320       }
321       
322       @Override
323       public IndexInput openInput(String name, int bufferSize) throws IOException {
324         // Make random changes to buffer size
325         bufferSize = 1+Math.abs(rand.nextInt() % 10);
326         IndexInput f = dir.openInput(name, bufferSize);
327         allIndexInputs.add(f);
328         return f;
329       }
330
331       @Override
332       public IndexOutput createOutput(String name) throws IOException {
333         return dir.createOutput(name);
334       }
335
336       @Override
337       public void close() throws IOException {
338         dir.close();
339       }
340
341       @Override
342       public void deleteFile(String name)
343         throws IOException
344       {
345         dir.deleteFile(name);
346       }
347       @Override
348       @Deprecated
349       /*  @deprecated Lucene never uses this API; it will be
350        *  removed in 4.0. */
351       public void touchFile(String name)
352         throws IOException
353       {
354         dir.touchFile(name);
355       }
356       @Override
357       public long fileModified(String name)
358         throws IOException
359       {
360         return dir.fileModified(name);
361       }
362       @Override
363       public boolean fileExists(String name)
364         throws IOException
365       {
366         return dir.fileExists(name);
367       }
368       @Override
369       public String[] listAll()
370         throws IOException
371       {
372         return dir.listAll();
373       }
374
375       @Override
376       public long fileLength(String name) throws IOException {
377         return dir.fileLength(name);
378       }
379
380
381     }
382 }