pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test / org / apache / lucene / index / TestCompoundFile.java
1 package org.apache.lucene.index;
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.IOException;
21 import java.io.File;
22
23 import org.apache.lucene.util.LuceneTestCase;
24 import org.apache.lucene.store.IndexOutput;
25 import org.apache.lucene.store.Directory;
26 import org.apache.lucene.store.IndexInput;
27 import org.apache.lucene.store.MockDirectoryWrapper;
28 import org.apache.lucene.store.MockDirectoryWrapper.Failure;
29 import org.apache.lucene.store.SimpleFSDirectory;
30 import org.apache.lucene.store._TestHelper;
31 import org.apache.lucene.util._TestUtil;
32
33
34 public class TestCompoundFile extends LuceneTestCase
35 {
36     private Directory dir;
37
38     @Override
39     public void setUp() throws Exception {
40        super.setUp();
41        File file = _TestUtil.getTempDir("testIndex");
42        // use a simple FSDir here, to be sure to have SimpleFSInputs
43        dir = new SimpleFSDirectory(file,null);
44     }
45
46     @Override
47     public void tearDown() throws Exception {
48        dir.close();
49        super.tearDown();
50     }
51
52     /** Creates a file of the specified size with random data. */
53     private void createRandomFile(Directory dir, String name, int size)
54     throws IOException
55     {
56         IndexOutput os = dir.createOutput(name);
57         for (int i=0; i<size; i++) {
58             byte b = (byte) (Math.random() * 256);
59             os.writeByte(b);
60         }
61         os.close();
62     }
63
64     /** Creates a file of the specified size with sequential data. The first
65      *  byte is written as the start byte provided. All subsequent bytes are
66      *  computed as start + offset where offset is the number of the byte.
67      */
68     private void createSequenceFile(Directory dir,
69                                     String name,
70                                     byte start,
71                                     int size)
72     throws IOException
73     {
74         IndexOutput os = dir.createOutput(name);
75         for (int i=0; i < size; i++) {
76             os.writeByte(start);
77             start ++;
78         }
79         os.close();
80     }
81
82
83     private void assertSameStreams(String msg,
84                                    IndexInput expected,
85                                    IndexInput test)
86     throws IOException
87     {
88         assertNotNull(msg + " null expected", expected);
89         assertNotNull(msg + " null test", test);
90         assertEquals(msg + " length", expected.length(), test.length());
91         assertEquals(msg + " position", expected.getFilePointer(),
92                                         test.getFilePointer());
93
94         byte expectedBuffer[] = new byte[512];
95         byte testBuffer[] = new byte[expectedBuffer.length];
96
97         long remainder = expected.length() - expected.getFilePointer();
98         while(remainder > 0) {
99             int readLen = (int) Math.min(remainder, expectedBuffer.length);
100             expected.readBytes(expectedBuffer, 0, readLen);
101             test.readBytes(testBuffer, 0, readLen);
102             assertEqualArrays(msg + ", remainder " + remainder, expectedBuffer,
103                 testBuffer, 0, readLen);
104             remainder -= readLen;
105         }
106     }
107
108
109     private void assertSameStreams(String msg,
110                                    IndexInput expected,
111                                    IndexInput actual,
112                                    long seekTo)
113     throws IOException
114     {
115         if(seekTo >= 0 && seekTo < expected.length())
116         {
117             expected.seek(seekTo);
118             actual.seek(seekTo);
119             assertSameStreams(msg + ", seek(mid)", expected, actual);
120         }
121     }
122
123
124
125     private void assertSameSeekBehavior(String msg,
126                                         IndexInput expected,
127                                         IndexInput actual)
128     throws IOException
129     {
130         // seek to 0
131         long point = 0;
132         assertSameStreams(msg + ", seek(0)", expected, actual, point);
133
134         // seek to middle
135         point = expected.length() / 2l;
136         assertSameStreams(msg + ", seek(mid)", expected, actual, point);
137
138         // seek to end - 2
139         point = expected.length() - 2;
140         assertSameStreams(msg + ", seek(end-2)", expected, actual, point);
141
142         // seek to end - 1
143         point = expected.length() - 1;
144         assertSameStreams(msg + ", seek(end-1)", expected, actual, point);
145
146         // seek to the end
147         point = expected.length();
148         assertSameStreams(msg + ", seek(end)", expected, actual, point);
149
150         // seek past end
151         point = expected.length() + 1;
152         assertSameStreams(msg + ", seek(end+1)", expected, actual, point);
153     }
154
155
156     private void assertEqualArrays(String msg,
157                                    byte[] expected,
158                                    byte[] test,
159                                    int start,
160                                    int len)
161     {
162         assertNotNull(msg + " null expected", expected);
163         assertNotNull(msg + " null test", test);
164
165         for (int i=start; i<len; i++) {
166             assertEquals(msg + " " + i, expected[i], test[i]);
167         }
168     }
169
170
171     // ===========================================================
172     //  Tests of the basic CompoundFile functionality
173     // ===========================================================
174
175
176     /** This test creates compound file based on a single file.
177      *  Files of different sizes are tested: 0, 1, 10, 100 bytes.
178      */
179     public void testSingleFile() throws IOException {
180         int data[] = new int[] { 0, 1, 10, 100 };
181         for (int i=0; i<data.length; i++) {
182             String name = "t" + data[i];
183             createSequenceFile(dir, name, (byte) 0, data[i]);
184             CompoundFileWriter csw = new CompoundFileWriter(dir, name + ".cfs");
185             csw.addFile(name);
186             csw.close();
187
188             CompoundFileReader csr = new CompoundFileReader(dir, name + ".cfs");
189             IndexInput expected = dir.openInput(name);
190             IndexInput actual = csr.openInput(name);
191             assertSameStreams(name, expected, actual);
192             assertSameSeekBehavior(name, expected, actual);
193             expected.close();
194             actual.close();
195             csr.close();
196         }
197     }
198
199
200     /** This test creates compound file based on two files.
201      *
202      */
203     public void testTwoFiles() throws IOException {
204         createSequenceFile(dir, "d1", (byte) 0, 15);
205         createSequenceFile(dir, "d2", (byte) 0, 114);
206
207         CompoundFileWriter csw = new CompoundFileWriter(dir, "d.csf");
208         csw.addFile("d1");
209         csw.addFile("d2");
210         csw.close();
211
212         CompoundFileReader csr = new CompoundFileReader(dir, "d.csf");
213         IndexInput expected = dir.openInput("d1");
214         IndexInput actual = csr.openInput("d1");
215         assertSameStreams("d1", expected, actual);
216         assertSameSeekBehavior("d1", expected, actual);
217         expected.close();
218         actual.close();
219
220         expected = dir.openInput("d2");
221         actual = csr.openInput("d2");
222         assertSameStreams("d2", expected, actual);
223         assertSameSeekBehavior("d2", expected, actual);
224         expected.close();
225         actual.close();
226         csr.close();
227     }
228
229     /** This test creates a compound file based on a large number of files of
230      *  various length. The file content is generated randomly. The sizes range
231      *  from 0 to 1Mb. Some of the sizes are selected to test the buffering
232      *  logic in the file reading code. For this the chunk variable is set to
233      *  the length of the buffer used internally by the compound file logic.
234      */
235     public void testRandomFiles() throws IOException {
236         // Setup the test segment
237         String segment = "test";
238         int chunk = 1024; // internal buffer size used by the stream
239         createRandomFile(dir, segment + ".zero", 0);
240         createRandomFile(dir, segment + ".one", 1);
241         createRandomFile(dir, segment + ".ten", 10);
242         createRandomFile(dir, segment + ".hundred", 100);
243         createRandomFile(dir, segment + ".big1", chunk);
244         createRandomFile(dir, segment + ".big2", chunk - 1);
245         createRandomFile(dir, segment + ".big3", chunk + 1);
246         createRandomFile(dir, segment + ".big4", 3 * chunk);
247         createRandomFile(dir, segment + ".big5", 3 * chunk - 1);
248         createRandomFile(dir, segment + ".big6", 3 * chunk + 1);
249         createRandomFile(dir, segment + ".big7", 1000 * chunk);
250
251         // Setup extraneous files
252         createRandomFile(dir, "onetwothree", 100);
253         createRandomFile(dir, segment + ".notIn", 50);
254         createRandomFile(dir, segment + ".notIn2", 51);
255
256         // Now test
257         CompoundFileWriter csw = new CompoundFileWriter(dir, "test.cfs");
258         final String data[] = new String[] {
259             ".zero", ".one", ".ten", ".hundred", ".big1", ".big2", ".big3",
260             ".big4", ".big5", ".big6", ".big7"
261         };
262         for (int i=0; i<data.length; i++) {
263             csw.addFile(segment + data[i]);
264         }
265         csw.close();
266
267         CompoundFileReader csr = new CompoundFileReader(dir, "test.cfs");
268         for (int i=0; i<data.length; i++) {
269             IndexInput check = dir.openInput(segment + data[i]);
270             IndexInput test = csr.openInput(segment + data[i]);
271             assertSameStreams(data[i], check, test);
272             assertSameSeekBehavior(data[i], check, test);
273             test.close();
274             check.close();
275         }
276         csr.close();
277     }
278
279
280     /** Setup a larger compound file with a number of components, each of
281      *  which is a sequential file (so that we can easily tell that we are
282      *  reading in the right byte). The methods sets up 20 files - f0 to f19,
283      *  the size of each file is 1000 bytes.
284      */
285     private void setUp_2() throws IOException {
286         CompoundFileWriter cw = new CompoundFileWriter(dir, "f.comp");
287         for (int i=0; i<20; i++) {
288             createSequenceFile(dir, "f" + i, (byte) 0, 2000);
289             cw.addFile("f" + i);
290         }
291         cw.close();
292     }
293
294
295     public void testReadAfterClose() throws IOException {
296         demo_FSIndexInputBug(dir, "test");
297     }
298
299     private void demo_FSIndexInputBug(Directory fsdir, String file)
300     throws IOException
301     {
302         // Setup the test file - we need more than 1024 bytes
303         IndexOutput os = fsdir.createOutput(file);
304         for(int i=0; i<2000; i++) {
305             os.writeByte((byte) i);
306         }
307         os.close();
308
309         IndexInput in = fsdir.openInput(file);
310
311         // This read primes the buffer in IndexInput
312         in.readByte();
313
314         // Close the file
315         in.close();
316
317         // ERROR: this call should fail, but succeeds because the buffer
318         // is still filled
319         in.readByte();
320
321         // ERROR: this call should fail, but succeeds for some reason as well
322         in.seek(1099);
323
324         try {
325             // OK: this call correctly fails. We are now past the 1024 internal
326             // buffer, so an actual IO is attempted, which fails
327             in.readByte();
328             fail("expected readByte() to throw exception");
329         } catch (IOException e) {
330           // expected exception
331         }
332     }
333
334
335     static boolean isCSIndexInput(IndexInput is) {
336         return is instanceof CompoundFileReader.CSIndexInput;
337     }
338
339     static boolean isCSIndexInputOpen(IndexInput is) throws IOException {
340         if (isCSIndexInput(is)) {
341             CompoundFileReader.CSIndexInput cis =
342             (CompoundFileReader.CSIndexInput) is;
343
344             return _TestHelper.isSimpleFSIndexInputOpen(cis.base);
345         } else {
346             return false;
347         }
348     }
349
350
351     public void testClonedStreamsClosing() throws IOException {
352         setUp_2();
353         CompoundFileReader cr = new CompoundFileReader(dir, "f.comp");
354
355         // basic clone
356         IndexInput expected = dir.openInput("f11");
357
358         // this test only works for FSIndexInput
359         assertTrue(_TestHelper.isSimpleFSIndexInput(expected));
360         assertTrue(_TestHelper.isSimpleFSIndexInputOpen(expected));
361
362         IndexInput one = cr.openInput("f11");
363         assertTrue(isCSIndexInputOpen(one));
364
365         IndexInput two = (IndexInput) one.clone();
366         assertTrue(isCSIndexInputOpen(two));
367
368         assertSameStreams("basic clone one", expected, one);
369         expected.seek(0);
370         assertSameStreams("basic clone two", expected, two);
371
372         // Now close the first stream
373         one.close();
374         assertTrue("Only close when cr is closed", isCSIndexInputOpen(one));
375
376         // The following should really fail since we couldn't expect to
377         // access a file once close has been called on it (regardless of
378         // buffering and/or clone magic)
379         expected.seek(0);
380         two.seek(0);
381         assertSameStreams("basic clone two/2", expected, two);
382
383
384         // Now close the compound reader
385         cr.close();
386         assertFalse("Now closed one", isCSIndexInputOpen(one));
387         assertFalse("Now closed two", isCSIndexInputOpen(two));
388
389         // The following may also fail since the compound stream is closed
390         expected.seek(0);
391         two.seek(0);
392         //assertSameStreams("basic clone two/3", expected, two);
393
394
395         // Now close the second clone
396         two.close();
397         expected.seek(0);
398         two.seek(0);
399         //assertSameStreams("basic clone two/4", expected, two);
400
401         expected.close();
402     }
403
404
405     /** This test opens two files from a compound stream and verifies that
406      *  their file positions are independent of each other.
407      */
408     public void testRandomAccess() throws IOException {
409         setUp_2();
410         CompoundFileReader cr = new CompoundFileReader(dir, "f.comp");
411
412         // Open two files
413         IndexInput e1 = dir.openInput("f11");
414         IndexInput e2 = dir.openInput("f3");
415
416         IndexInput a1 = cr.openInput("f11");
417         IndexInput a2 = dir.openInput("f3");
418
419         // Seek the first pair
420         e1.seek(100);
421         a1.seek(100);
422         assertEquals(100, e1.getFilePointer());
423         assertEquals(100, a1.getFilePointer());
424         byte be1 = e1.readByte();
425         byte ba1 = a1.readByte();
426         assertEquals(be1, ba1);
427
428         // Now seek the second pair
429         e2.seek(1027);
430         a2.seek(1027);
431         assertEquals(1027, e2.getFilePointer());
432         assertEquals(1027, a2.getFilePointer());
433         byte be2 = e2.readByte();
434         byte ba2 = a2.readByte();
435         assertEquals(be2, ba2);
436
437         // Now make sure the first one didn't move
438         assertEquals(101, e1.getFilePointer());
439         assertEquals(101, a1.getFilePointer());
440         be1 = e1.readByte();
441         ba1 = a1.readByte();
442         assertEquals(be1, ba1);
443
444         // Now more the first one again, past the buffer length
445         e1.seek(1910);
446         a1.seek(1910);
447         assertEquals(1910, e1.getFilePointer());
448         assertEquals(1910, a1.getFilePointer());
449         be1 = e1.readByte();
450         ba1 = a1.readByte();
451         assertEquals(be1, ba1);
452
453         // Now make sure the second set didn't move
454         assertEquals(1028, e2.getFilePointer());
455         assertEquals(1028, a2.getFilePointer());
456         be2 = e2.readByte();
457         ba2 = a2.readByte();
458         assertEquals(be2, ba2);
459
460         // Move the second set back, again cross the buffer size
461         e2.seek(17);
462         a2.seek(17);
463         assertEquals(17, e2.getFilePointer());
464         assertEquals(17, a2.getFilePointer());
465         be2 = e2.readByte();
466         ba2 = a2.readByte();
467         assertEquals(be2, ba2);
468
469         // Finally, make sure the first set didn't move
470         // Now make sure the first one didn't move
471         assertEquals(1911, e1.getFilePointer());
472         assertEquals(1911, a1.getFilePointer());
473         be1 = e1.readByte();
474         ba1 = a1.readByte();
475         assertEquals(be1, ba1);
476
477         e1.close();
478         e2.close();
479         a1.close();
480         a2.close();
481         cr.close();
482     }
483
484     /** This test opens two files from a compound stream and verifies that
485      *  their file positions are independent of each other.
486      */
487     public void testRandomAccessClones() throws IOException {
488         setUp_2();
489         CompoundFileReader cr = new CompoundFileReader(dir, "f.comp");
490
491         // Open two files
492         IndexInput e1 = cr.openInput("f11");
493         IndexInput e2 = cr.openInput("f3");
494
495         IndexInput a1 = (IndexInput) e1.clone();
496         IndexInput a2 = (IndexInput) e2.clone();
497
498         // Seek the first pair
499         e1.seek(100);
500         a1.seek(100);
501         assertEquals(100, e1.getFilePointer());
502         assertEquals(100, a1.getFilePointer());
503         byte be1 = e1.readByte();
504         byte ba1 = a1.readByte();
505         assertEquals(be1, ba1);
506
507         // Now seek the second pair
508         e2.seek(1027);
509         a2.seek(1027);
510         assertEquals(1027, e2.getFilePointer());
511         assertEquals(1027, a2.getFilePointer());
512         byte be2 = e2.readByte();
513         byte ba2 = a2.readByte();
514         assertEquals(be2, ba2);
515
516         // Now make sure the first one didn't move
517         assertEquals(101, e1.getFilePointer());
518         assertEquals(101, a1.getFilePointer());
519         be1 = e1.readByte();
520         ba1 = a1.readByte();
521         assertEquals(be1, ba1);
522
523         // Now more the first one again, past the buffer length
524         e1.seek(1910);
525         a1.seek(1910);
526         assertEquals(1910, e1.getFilePointer());
527         assertEquals(1910, a1.getFilePointer());
528         be1 = e1.readByte();
529         ba1 = a1.readByte();
530         assertEquals(be1, ba1);
531
532         // Now make sure the second set didn't move
533         assertEquals(1028, e2.getFilePointer());
534         assertEquals(1028, a2.getFilePointer());
535         be2 = e2.readByte();
536         ba2 = a2.readByte();
537         assertEquals(be2, ba2);
538
539         // Move the second set back, again cross the buffer size
540         e2.seek(17);
541         a2.seek(17);
542         assertEquals(17, e2.getFilePointer());
543         assertEquals(17, a2.getFilePointer());
544         be2 = e2.readByte();
545         ba2 = a2.readByte();
546         assertEquals(be2, ba2);
547
548         // Finally, make sure the first set didn't move
549         // Now make sure the first one didn't move
550         assertEquals(1911, e1.getFilePointer());
551         assertEquals(1911, a1.getFilePointer());
552         be1 = e1.readByte();
553         ba1 = a1.readByte();
554         assertEquals(be1, ba1);
555
556         e1.close();
557         e2.close();
558         a1.close();
559         a2.close();
560         cr.close();
561     }
562
563
564     public void testFileNotFound() throws IOException {
565         setUp_2();
566         CompoundFileReader cr = new CompoundFileReader(dir, "f.comp");
567
568         // Open two files
569         try {
570             cr.openInput("bogus");
571             fail("File not found");
572
573         } catch (IOException e) {
574             /* success */
575             //System.out.println("SUCCESS: File Not Found: " + e);
576         }
577
578         cr.close();
579     }
580
581
582     public void testReadPastEOF() throws IOException {
583         setUp_2();
584         CompoundFileReader cr = new CompoundFileReader(dir, "f.comp");
585         IndexInput is = cr.openInput("f2");
586         is.seek(is.length() - 10);
587         byte b[] = new byte[100];
588         is.readBytes(b, 0, 10);
589
590         try {
591             is.readByte();
592             fail("Single byte read past end of file");
593         } catch (IOException e) {
594             /* success */
595             //System.out.println("SUCCESS: single byte read past end of file: " + e);
596         }
597
598         is.seek(is.length() - 10);
599         try {
600             is.readBytes(b, 0, 50);
601             fail("Block read past end of file");
602         } catch (IOException e) {
603             /* success */
604             //System.out.println("SUCCESS: block read past end of file: " + e);
605         }
606
607         is.close();
608         cr.close();
609     }
610
611     /** This test that writes larger than the size of the buffer output
612      * will correctly increment the file pointer.
613      */
614     public void testLargeWrites() throws IOException {
615         IndexOutput os = dir.createOutput("testBufferStart.txt");
616
617         byte[] largeBuf = new byte[2048];
618         for (int i=0; i<largeBuf.length; i++) {
619             largeBuf[i] = (byte) (Math.random() * 256);
620         }
621
622         long currentPos = os.getFilePointer();
623         os.writeBytes(largeBuf, largeBuf.length);
624
625         try {
626             assertEquals(currentPos + largeBuf.length, os.getFilePointer());
627         } finally {
628             os.close();
629         }
630
631     }
632     
633    public void testAddExternalFile() throws IOException {
634        createSequenceFile(dir, "d1", (byte) 0, 15);
635
636        Directory newDir = newDirectory();
637        CompoundFileWriter csw = new CompoundFileWriter(newDir, "d.csf");
638        csw.addFile("d1", dir);
639        csw.close();
640
641        CompoundFileReader csr = new CompoundFileReader(newDir, "d.csf");
642        IndexInput expected = dir.openInput("d1");
643        IndexInput actual = csr.openInput("d1");
644        assertSameStreams("d1", expected, actual);
645        assertSameSeekBehavior("d1", expected, actual);
646        expected.close();
647        actual.close();
648        csr.close();
649        
650        newDir.close();
651    }
652
653   // Make sure we don't somehow use more than 1 descriptor
654   // when reading a CFS with many subs:
655   public void testManySubFiles() throws IOException {
656
657     final Directory d = newFSDirectory(_TestUtil.getTempDir("CFSManySubFiles"));
658     final int FILE_COUNT = 10000;
659
660     for(int fileIdx=0;fileIdx<FILE_COUNT;fileIdx++) {
661       IndexOutput out = d.createOutput("file." + fileIdx);
662       out.writeByte((byte) fileIdx);
663       out.close();
664     }
665     
666     final CompoundFileWriter cfw = new CompoundFileWriter(d, "c.cfs");
667     for(int fileIdx=0;fileIdx<FILE_COUNT;fileIdx++) {
668       cfw.addFile("file." + fileIdx);
669     }
670     cfw.close();
671
672     final IndexInput[] ins = new IndexInput[FILE_COUNT];
673     final CompoundFileReader cfr = new CompoundFileReader(d, "c.cfs");
674     for(int fileIdx=0;fileIdx<FILE_COUNT;fileIdx++) {
675       ins[fileIdx] = cfr.openInput("file." + fileIdx);
676     }
677
678     for(int fileIdx=0;fileIdx<FILE_COUNT;fileIdx++) {
679       assertEquals((byte) fileIdx, ins[fileIdx].readByte());
680     }
681
682     for(int fileIdx=0;fileIdx<FILE_COUNT;fileIdx++) {
683       ins[fileIdx].close();
684     }
685     cfr.close();
686     d.close();
687   }
688 }