blob: fca0da8cbe92a508031b7ed80f5cfee688396abd [file] [log] [blame]
/*
* 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.
*/
package org.apache.lucene.store;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.zip.CRC32;
import com.carrotsearch.randomizedtesting.RandomizedTest;
import com.carrotsearch.randomizedtesting.generators.RandomBytes;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexFileNames;
import org.apache.lucene.index.IndexNotFoundException;
import org.apache.lucene.mockfile.ExtrasFS;
import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.TestUtil;
import org.junit.Assert;
/**
* Base class for per-Directory tests.
*/
public abstract class BaseDirectoryTestCase extends LuceneTestCase {
/** A subclass returns the Directory to be tested; if it's
* an FS-based directory it should point to the specified
* path, else it can ignore it. */
protected abstract Directory getDirectory(Path path) throws IOException;
public void testCopyFrom() throws Exception {
try (Directory source = getDirectory(createTempDir("testCopy"));
Directory dest = newDirectory()) {
runCopyFrom(source, dest);
}
try (Directory source = newDirectory();
Directory dest = getDirectory(createTempDir("testCopyDestination"))) {
runCopyFrom(source, dest);
}
}
private void runCopyFrom(Directory source, Directory dest) throws IOException {
IndexOutput output = source.createOutput("foobar", newIOContext(random()));
byte bytes[] = RandomBytes.randomBytesOfLength(random(), 20000);
output.writeBytes(bytes, bytes.length);
output.close();
dest.copyFrom(source, "foobar", "foobaz", newIOContext(random()));
assertTrue(slowFileExists(dest, "foobaz"));
IndexInput input = dest.openInput("foobaz", newIOContext(random()));
byte bytes2[] = new byte[bytes.length];
input.readBytes(bytes2, 0, bytes2.length);
input.close();
assertArrayEquals(bytes, bytes2);
}
public void testRename() throws Exception {
try (Directory dir = getDirectory(createTempDir("testRename"))) {
IndexOutput output = dir.createOutput("foobar", newIOContext(random()));
int numBytes = random().nextInt(20000);
byte bytes[] = new byte[numBytes];
random().nextBytes(bytes);
output.writeBytes(bytes, bytes.length);
output.close();
dir.rename("foobar", "foobaz");
IndexInput input = dir.openInput("foobaz", newIOContext(random()));
byte bytes2[] = new byte[numBytes];
input.readBytes(bytes2, 0, bytes2.length);
assertEquals(input.length(), numBytes);
input.close();
assertArrayEquals(bytes, bytes2);
}
}
public void testDeleteFile() throws Exception {
try (Directory dir = getDirectory(createTempDir("testDeleteFile"))) {
String file = "foo.txt";
Assert.assertFalse(Arrays.asList(dir.listAll()).contains(file));
dir.createOutput("foo.txt", IOContext.DEFAULT).close();
Assert.assertTrue(Arrays.asList(dir.listAll()).contains(file));
dir.deleteFile("foo.txt");
Assert.assertFalse(Arrays.asList(dir.listAll()).contains(file));
expectThrowsAnyOf(Arrays.asList(NoSuchFileException.class, FileNotFoundException.class), () -> {
dir.deleteFile("foo.txt");
});
}
}
public void testByte() throws Exception {
try (Directory dir = getDirectory(createTempDir("testByte"))) {
IndexOutput output = dir.createOutput("byte", newIOContext(random()));
output.writeByte((byte) 128);
output.close();
IndexInput input = dir.openInput("byte", newIOContext(random()));
assertEquals(1, input.length());
assertEquals((byte) 128, input.readByte());
input.close();
}
}
public void testShort() throws Exception {
try (Directory dir = getDirectory(createTempDir("testShort"))) {
IndexOutput output = dir.createOutput("short", newIOContext(random()));
output.writeShort((short) -20);
output.close();
IndexInput input = dir.openInput("short", newIOContext(random()));
assertEquals(2, input.length());
assertEquals((short) -20, input.readShort());
input.close();
}
}
public void testInt() throws Exception {
try (Directory dir = getDirectory(createTempDir("testInt"))) {
IndexOutput output = dir.createOutput("int", newIOContext(random()));
output.writeInt(-500);
output.close();
IndexInput input = dir.openInput("int", newIOContext(random()));
assertEquals(4, input.length());
assertEquals(-500, input.readInt());
input.close();
}
}
public void testLong() throws Exception {
try (Directory dir = getDirectory(createTempDir("testLong"))) {
IndexOutput output = dir.createOutput("long", newIOContext(random()));
output.writeLong(-5000);
output.close();
IndexInput input = dir.openInput("long", newIOContext(random()));
assertEquals(8, input.length());
assertEquals(-5000L, input.readLong());
input.close();
}
}
public void testAlignedLittleEndianLongs() throws Exception {
try (Directory dir = getDirectory(createTempDir("testAlignedLittleEndianLongs"))) {
try (IndexOutput out = dir.createOutput("littleEndianLongs", newIOContext(random()))) {
out.writeLong(Long.reverseBytes(3L));
out.writeLong(Long.reverseBytes(Long.MAX_VALUE));
out.writeLong(Long.reverseBytes(-3L));
}
try (IndexInput input = dir.openInput("littleEndianLongs", newIOContext(random()))) {
assertEquals(24, input.length());
long[] l = new long[4];
input.readLELongs(l, 1, 3);
assertArrayEquals(new long[] {0L, 3L, Long.MAX_VALUE, -3L}, l);
assertEquals(24, input.getFilePointer());
}
}
}
public void testUnalignedLittleEndianLongs() throws Exception {
try (Directory dir = getDirectory(createTempDir("testUnalignedLittleEndianLongs"))) {
try (IndexOutput out = dir.createOutput("littleEndianLongs", newIOContext(random()))) {
out.writeByte((byte) 2);
out.writeLong(Long.reverseBytes(3L));
out.writeLong(Long.reverseBytes(Long.MAX_VALUE));
out.writeLong(Long.reverseBytes(-3L));
}
try (IndexInput input = dir.openInput("littleEndianLongs", newIOContext(random()))) {
assertEquals(25, input.length());
assertEquals(2, input.readByte());
long[] l = new long[4];
input.readLELongs(l, 1, 3);
assertArrayEquals(new long[] {0L, 3L, Long.MAX_VALUE, -3L}, l);
assertEquals(25, input.getFilePointer());
}
}
}
public void testLittleEndianLongsUnderflow() throws Exception {
try (Directory dir = getDirectory(createTempDir("testLittleEndianLongsUnderflow"))) {
final int offset = random().nextInt(8);
final int length = TestUtil.nextInt(random(), 1, 16);
try (IndexOutput out = dir.createOutput("littleEndianLongs", newIOContext(random()))) {
byte[] b = new byte[offset + length * Long.BYTES - TestUtil.nextInt(random(), 1, Long.BYTES)];
random().nextBytes(b);
out.writeBytes(b, b.length);
}
try (IndexInput input = dir.openInput("littleEndianLongs", newIOContext(random()))) {
input.seek(offset);
expectThrows(EOFException.class,
() -> input.readLELongs(new long[length], 0, length));
}
}
}
public void testString() throws Exception {
try (Directory dir = getDirectory(createTempDir("testString"))) {
IndexOutput output = dir.createOutput("string", newIOContext(random()));
output.writeString("hello!");
output.close();
IndexInput input = dir.openInput("string", newIOContext(random()));
assertEquals("hello!", input.readString());
assertEquals(7, input.length());
input.close();
}
}
public void testVInt() throws Exception {
try (Directory dir = getDirectory(createTempDir("testVInt"))) {
IndexOutput output = dir.createOutput("vint", newIOContext(random()));
output.writeVInt(500);
output.close();
IndexInput input = dir.openInput("vint", newIOContext(random()));
assertEquals(2, input.length());
assertEquals(500, input.readVInt());
input.close();
}
}
public void testVLong() throws Exception {
try (Directory dir = getDirectory(createTempDir("testVLong"))) {
IndexOutput output = dir.createOutput("vlong", newIOContext(random()));
output.writeVLong(Long.MAX_VALUE);
output.close();
IndexInput input = dir.openInput("vlong", newIOContext(random()));
assertEquals(9, input.length());
assertEquals(Long.MAX_VALUE, input.readVLong());
input.close();
}
}
public void testZInt() throws Exception {
final int[] ints = new int[random().nextInt(10)];
for (int i = 0; i < ints.length; ++i) {
switch (random().nextInt(3)) {
case 0:
ints[i] = random().nextInt();
break;
case 1:
ints[i] = random().nextBoolean() ? Integer.MIN_VALUE : Integer.MAX_VALUE;
break;
case 2:
ints[i] = (random().nextBoolean() ? -1 : 1) * random().nextInt(1024);
break;
default:
throw new AssertionError();
}
}
try (Directory dir = getDirectory(createTempDir("testZInt"))) {
IndexOutput output = dir.createOutput("zint", newIOContext(random()));
for (int i : ints) {
output.writeZInt(i);
}
output.close();
IndexInput input = dir.openInput("zint", newIOContext(random()));
for (int i : ints) {
assertEquals(i, input.readZInt());
}
assertEquals(input.length(), input.getFilePointer());
input.close();
}
}
public void testZLong() throws Exception {
final long[] longs = new long[random().nextInt(10)];
for (int i = 0; i < longs.length; ++i) {
switch (random().nextInt(3)) {
case 0:
longs[i] = random().nextLong();
break;
case 1:
longs[i] = random().nextBoolean() ? Long.MIN_VALUE : Long.MAX_VALUE;
break;
case 2:
longs[i] = (random().nextBoolean() ? -1 : 1) * random().nextInt(1024);
break;
default:
throw new AssertionError();
}
}
try (Directory dir = getDirectory(createTempDir("testZLong"))) {
IndexOutput output = dir.createOutput("zlong", newIOContext(random()));
for (long l : longs) {
output.writeZLong(l);
}
output.close();
IndexInput input = dir.openInput("zlong", newIOContext(random()));
for (long l : longs) {
assertEquals(l, input.readZLong());
}
assertEquals(input.length(), input.getFilePointer());
input.close();
}
}
public void testSetOfStrings() throws Exception {
try (Directory dir = getDirectory(createTempDir("testSetOfStrings"))) {
IndexOutput output = dir.createOutput("stringset", newIOContext(random()));
output.writeSetOfStrings(asSet("test1", "test2"));
output.writeSetOfStrings(Collections.emptySet());
output.writeSetOfStrings(asSet("test3"));
output.close();
IndexInput input = dir.openInput("stringset", newIOContext(random()));
Set<String> set1 = input.readSetOfStrings();
assertEquals(asSet("test1", "test2"), set1);
// set should be immutable
expectThrows(UnsupportedOperationException.class, () -> {
set1.add("bogus");
});
Set<String> set2 = input.readSetOfStrings();
assertEquals(Collections.emptySet(), set2);
// set should be immutable
expectThrows(UnsupportedOperationException.class, () -> {
set2.add("bogus");
});
Set<String> set3 = input.readSetOfStrings();
assertEquals(Collections.singleton("test3"), set3);
// set should be immutable
expectThrows(UnsupportedOperationException.class, () -> {
set3.add("bogus");
});
assertEquals(input.length(), input.getFilePointer());
input.close();
}
}
public void testMapOfStrings() throws Exception {
Map<String,String> m = new HashMap<>();
m.put("test1", "value1");
m.put("test2", "value2");
try (Directory dir = getDirectory(createTempDir("testMapOfStrings"))) {
IndexOutput output = dir.createOutput("stringmap", newIOContext(random()));
output.writeMapOfStrings(m);
output.writeMapOfStrings(Collections.emptyMap());
output.writeMapOfStrings(Collections.singletonMap("key", "value"));
output.close();
IndexInput input = dir.openInput("stringmap", newIOContext(random()));
Map<String, String> map1 = input.readMapOfStrings();
assertEquals(m, map1);
// map should be immutable
expectThrows(UnsupportedOperationException.class, () -> {
map1.put("bogus1", "bogus2");
});
Map<String, String> map2 = input.readMapOfStrings();
assertEquals(Collections.emptyMap(), map2);
// map should be immutable
expectThrows(UnsupportedOperationException.class, () -> {
map2.put("bogus1", "bogus2");
});
Map<String, String> map3 = input.readMapOfStrings();
assertEquals(Collections.singletonMap("key", "value"), map3);
// map should be immutable
expectThrows(UnsupportedOperationException.class, () -> {
map3.put("bogus1", "bogus2");
});
assertEquals(input.length(), input.getFilePointer());
input.close();
}
}
// TODO: fold in some of the testing of o.a.l.index.TestIndexInput in here!
public void testChecksum() throws Exception {
CRC32 expected = new CRC32();
int numBytes = random().nextInt(20000);
byte bytes[] = new byte[numBytes];
random().nextBytes(bytes);
expected.update(bytes);
try (Directory dir = getDirectory(createTempDir("testChecksum"))) {
IndexOutput output = dir.createOutput("checksum", newIOContext(random()));
output.writeBytes(bytes, 0, bytes.length);
output.close();
ChecksumIndexInput input = dir.openChecksumInput("checksum", newIOContext(random()));
input.skipBytes(numBytes);
assertEquals(expected.getValue(), input.getChecksum());
input.close();
}
}
/** Make sure directory throws AlreadyClosedException if
* you try to createOutput after closing. */
public void testDetectClose() throws Throwable {
Directory dir = getDirectory(createTempDir("testDetectClose"));
dir.close();
expectThrows(AlreadyClosedException.class, () -> {
dir.createOutput("test", newIOContext(random()));
});
}
public void testThreadSafetyInListAll() throws Exception {
try (Directory dir = getDirectory(createTempDir("testThreadSafety"))) {
if (dir instanceof BaseDirectoryWrapper) {
// we are not making a real index, just writing, reading files.
((BaseDirectoryWrapper) dir).setCheckIndexOnClose(false);
}
if (dir instanceof MockDirectoryWrapper) {
// makes this test really slow
((MockDirectoryWrapper) dir).setThrottling(MockDirectoryWrapper.Throttling.NEVER);
}
AtomicBoolean stop = new AtomicBoolean();
Thread writer = new Thread(() -> {
try {
Random rnd = new Random(RandomizedTest.randomLong() + 1);
for (int i = 0, max = RandomizedTest.randomIntBetween(500, 1000); i < max; i++) {
String fileName = "file-" + i;
try (IndexOutput output = dir.createOutput(fileName, newIOContext(random()))) {
assert output != null;
// Add some lags so that the other thread can read the content of the directory.
Thread.yield();
}
assertTrue(slowFileExists(dir, fileName));
}
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
stop.set(true);
}
});
Thread reader = new Thread(() -> {
try {
Random rnd = new Random(RandomizedTest.randomLong());
while (!stop.get()) {
String [] files = Arrays.stream(dir.listAll())
.filter(name -> !ExtrasFS.isExtra(name)) // Ignore anything from ExtraFS.
.toArray(String[]::new);
if (files.length > 0) {
do {
String file = RandomPicks.randomFrom(rnd, files);
try (IndexInput input = dir.openInput(file, newIOContext(random()))) {
// Just open, nothing else.
assert input != null;
} catch (AccessDeniedException e) {
// Access denied is allowed for files for which the output is still open (MockDirectoryWriter enforces
// this, for example). Since we don't synchronize with the writer thread, just ignore it.
} catch (IOException e) {
throw new UncheckedIOException("Something went wrong when opening: " + file, e);
}
} while (rnd.nextInt(3) != 0); // Sometimes break and list files again.
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
reader.start();
writer.start();
writer.join();
reader.join();
}
}
/**
* LUCENE-1468: once we create an output, we should see
* it in the dir listing.
*/
public void testFileExistsInListAfterCreated() throws IOException {
try (Directory dir = getDirectory(createTempDir("testFileExistsInListAfterCreated"))) {
String name = "file";
dir.createOutput(name, newIOContext(random())).close();
assertTrue(slowFileExists(dir, name));
assertTrue(Arrays.asList(dir.listAll()).contains(name));
}
}
// LUCENE-2852
public void testSeekToEOFThenBack() throws Exception {
try (Directory dir = getDirectory(createTempDir("testSeekToEOFThenBack"))) {
int bufferLength = 1024;
IndexOutput o = dir.createOutput("out", newIOContext(random()));
byte[] bytes = new byte[3 * bufferLength];
o.writeBytes(bytes, 0, bytes.length);
o.close();
IndexInput i = dir.openInput("out", newIOContext(random()));
i.seek(2 * bufferLength - 1);
i.seek(3 * bufferLength);
i.seek(bufferLength);
i.readBytes(bytes, 0, 2 * bufferLength);
i.close();
}
}
// LUCENE-1196
public void testIllegalEOF() throws Exception {
try (Directory dir = getDirectory(createTempDir("testIllegalEOF"))) {
IndexOutput o = dir.createOutput("out", newIOContext(random()));
byte[] b = new byte[1024];
o.writeBytes(b, 0, 1024);
o.close();
IndexInput i = dir.openInput("out", newIOContext(random()));
i.seek(1024);
i.close();
}
}
public void testSeekPastEOF() throws Exception {
try (Directory dir = getDirectory(createTempDir("testSeekPastEOF"))) {
IndexOutput o = dir.createOutput("out", newIOContext(random()));
final int len = random().nextInt(2048);
byte[] b = new byte[len];
o.writeBytes(b, 0, len);
o.close();
IndexInput i = dir.openInput("out", newIOContext(random()));
// Seeking past EOF should always throw EOFException
expectThrows(EOFException.class, () -> i.seek(len + RandomizedTest.randomIntBetween(1, 2048)));
// Seeking exactly to EOF should never throw any exception.
i.seek(len);
// But any read following the seek(len) should throw an EOFException.
expectThrows(EOFException.class, i::readByte);
expectThrows(EOFException.class, () -> {
i.readBytes(new byte [1], 0, 1);
});
i.close();
}
}
public void testSliceOutOfBounds() throws Exception {
try (Directory dir = getDirectory(createTempDir("testSliceOutOfBounds"))) {
IndexOutput o = dir.createOutput("out", newIOContext(random()));
final int len = random().nextInt(2040) + 8;
byte[] b = new byte[len];
o.writeBytes(b, 0, len);
o.close();
IndexInput i = dir.openInput("out", newIOContext(random()));
expectThrows(IllegalArgumentException.class, () -> {
i.slice("slice1", 0, len + 1);
});
expectThrows(IllegalArgumentException.class, () -> {
i.slice("slice2", -1, len);
});
IndexInput slice = i.slice("slice3", 4, len / 2);
expectThrows(IllegalArgumentException.class, () -> {
slice.slice("slice3sub", 1, len / 2);
});
i.close();
}
}
// LUCENE-3382 -- make sure we get exception if the directory really does not exist.
public void testNoDir() throws Throwable {
Path tempDir = createTempDir("doesnotexist");
IOUtils.rm(tempDir);
try (Directory dir = getDirectory(tempDir)) {
expectThrowsAnyOf(Arrays.asList(NoSuchFileException.class, IndexNotFoundException.class), () -> {
DirectoryReader.open(dir);
});
}
}
public void testCopyBytes() throws Exception {
try (Directory dir = getDirectory(createTempDir("testCopyBytes"))) {
IndexOutput out = dir.createOutput("test", newIOContext(random()));
byte[] bytes = new byte[TestUtil.nextInt(random(), 1, 77777)];
final int size = TestUtil.nextInt(random(), 1, 1777777);
int upto = 0;
int byteUpto = 0;
while (upto < size) {
bytes[byteUpto++] = value(upto);
upto++;
if (byteUpto == bytes.length) {
out.writeBytes(bytes, 0, bytes.length);
byteUpto = 0;
}
}
out.writeBytes(bytes, 0, byteUpto);
assertEquals(size, out.getFilePointer());
out.close();
assertEquals(size, dir.fileLength("test"));
// copy from test -> test2
final IndexInput in = dir.openInput("test", newIOContext(random()));
out = dir.createOutput("test2", newIOContext(random()));
upto = 0;
while (upto < size) {
if (random().nextBoolean()) {
out.writeByte(in.readByte());
upto++;
} else {
final int chunk = Math.min(
TestUtil.nextInt(random(), 1, bytes.length), size - upto);
out.copyBytes(in, chunk);
upto += chunk;
}
}
assertEquals(size, upto);
out.close();
in.close();
// verify
IndexInput in2 = dir.openInput("test2", newIOContext(random()));
upto = 0;
while (upto < size) {
if (random().nextBoolean()) {
final byte v = in2.readByte();
assertEquals(value(upto), v);
upto++;
} else {
final int limit = Math.min(
TestUtil.nextInt(random(), 1, bytes.length), size - upto);
in2.readBytes(bytes, 0, limit);
for (int byteIdx = 0; byteIdx < limit; byteIdx++) {
assertEquals(value(upto), bytes[byteIdx]);
upto++;
}
}
}
in2.close();
dir.deleteFile("test");
dir.deleteFile("test2");
}
}
private static byte value(int idx) {
return (byte) ((idx % 256) * (1 + (idx / 256)));
}
// LUCENE-3541
public void testCopyBytesWithThreads() throws Exception {
try (Directory d = getDirectory(createTempDir("testCopyBytesWithThreads"))) {
byte data[] = RandomBytes.randomBytesOfLengthBetween(random(), 101, 10000);
IndexOutput output = d.createOutput("data", IOContext.DEFAULT);
output.writeBytes(data, 0, data.length);
output.close();
IndexInput input = d.openInput("data", IOContext.DEFAULT);
IndexOutput outputHeader = d.createOutput("header", IOContext.DEFAULT);
// copy our 100-byte header
outputHeader.copyBytes(input, 100);
outputHeader.close();
// now make N copies of the remaining bytes
int threads = 10;
CyclicBarrier start = new CyclicBarrier(threads);
Thread copies [] = IntStream.range(0, threads)
.mapToObj((i) -> {
IndexInput src = input.clone();
Thread t = new Thread(() -> {
try {
start.await();
IndexOutput dst = d.createOutput("copy" + i, IOContext.DEFAULT);
dst.copyBytes(src, src.length() - 100);
dst.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
t.start();
return t;
})
.toArray(Thread[]::new);
for (Thread t : copies) {
t.join();
}
for (int i = 0; i < threads; i++) {
try (IndexInput copiedData = d.openInput("copy" + i, IOContext.DEFAULT)) {
byte[] dataCopy = new byte[data.length];
System.arraycopy(data, 0, dataCopy, 0, 100);
copiedData.readBytes(dataCopy, 100, data.length - 100);
assertArrayEquals(data, dataCopy);
}
}
input.close();
}
}
// this test backdoors the directory via the filesystem. so it must actually use the filesystem
// TODO: somehow change this test to
public void testFsyncDoesntCreateNewFiles() throws Exception {
Path path = createTempDir("nocreate");
try (Directory fsdir = getDirectory(path)) {
// this test backdoors the directory via the filesystem. so it must be an FSDir (for now)
// TODO: figure a way to test this better/clean it up. E.g. we should be testing for FileSwitchDir,
// if it's using two FSdirs and so on
if (fsdir instanceof FSDirectory == false) {
assumeTrue("test only works for FSDirectory subclasses", false);
return;
}
// create a file
IndexOutput out = fsdir.createOutput("afile", newIOContext(random()));
out.writeString("boo");
out.close();
// delete it in the file system.
Files.delete(path.resolve("afile"));
int fileCount = fsdir.listAll().length;
// fsync it
expectThrowsAnyOf(Arrays.asList(FileNotFoundException.class, NoSuchFileException.class), () -> {
fsdir.sync(Collections.singleton("afile"));
});
// no new files created
assertEquals(fileCount, fsdir.listAll().length);
}
}
// random access APIs
public void testRandomLong() throws Exception {
try (Directory dir = getDirectory(createTempDir("testLongs"))) {
IndexOutput output = dir.createOutput("longs", newIOContext(random()));
int num = TestUtil.nextInt(random(), 50, 3000);
long longs[] = new long[num];
for (int i = 0; i < longs.length; i++) {
longs[i] = TestUtil.nextLong(random(), Long.MIN_VALUE, Long.MAX_VALUE);
output.writeLong(longs[i]);
}
output.close();
// slice
IndexInput input = dir.openInput("longs", newIOContext(random()));
RandomAccessInput slice = input.randomAccessSlice(0, input.length());
for (int i = 0; i < longs.length; i++) {
assertEquals(longs[i], slice.readLong(i * 8));
}
// subslices
for (int i = 1; i < longs.length; i++) {
long offset = i * 8;
RandomAccessInput subslice = input.randomAccessSlice(offset, input.length() - offset);
for (int j = i; j < longs.length; j++) {
assertEquals(longs[j], subslice.readLong((j - i) * 8));
}
}
// with padding
for (int i = 0; i < 7; i++) {
String name = "longs-" + i;
IndexOutput o = dir.createOutput(name, newIOContext(random()));
byte junk[] = new byte[i];
random().nextBytes(junk);
o.writeBytes(junk, junk.length);
input.seek(0);
o.copyBytes(input, input.length());
o.close();
IndexInput padded = dir.openInput(name, newIOContext(random()));
RandomAccessInput whole = padded.randomAccessSlice(i, padded.length() - i);
for (int j = 0; j < longs.length; j++) {
assertEquals(longs[j], whole.readLong(j * 8));
}
padded.close();
}
input.close();
}
}
public void testRandomInt() throws Exception {
try (Directory dir = getDirectory(createTempDir("testInts"))) {
IndexOutput output = dir.createOutput("ints", newIOContext(random()));
int num = TestUtil.nextInt(random(), 50, 3000);
int ints[] = new int[num];
for (int i = 0; i < ints.length; i++) {
ints[i] = random().nextInt();
output.writeInt(ints[i]);
}
output.close();
// slice
IndexInput input = dir.openInput("ints", newIOContext(random()));
RandomAccessInput slice = input.randomAccessSlice(0, input.length());
for (int i = 0; i < ints.length; i++) {
assertEquals(ints[i], slice.readInt(i * 4));
}
// subslices
for (int i = 1; i < ints.length; i++) {
long offset = i * 4;
RandomAccessInput subslice = input.randomAccessSlice(offset, input.length() - offset);
for (int j = i; j < ints.length; j++) {
assertEquals(ints[j], subslice.readInt((j - i) * 4));
}
}
// with padding
for (int i = 0; i < 7; i++) {
String name = "ints-" + i;
IndexOutput o = dir.createOutput(name, newIOContext(random()));
byte junk[] = new byte[i];
random().nextBytes(junk);
o.writeBytes(junk, junk.length);
input.seek(0);
o.copyBytes(input, input.length());
o.close();
IndexInput padded = dir.openInput(name, newIOContext(random()));
RandomAccessInput whole = padded.randomAccessSlice(i, padded.length() - i);
for (int j = 0; j < ints.length; j++) {
assertEquals(ints[j], whole.readInt(j * 4));
}
padded.close();
}
input.close();
}
}
public void testRandomShort() throws Exception {
try (Directory dir = getDirectory(createTempDir("testShorts"))) {
IndexOutput output = dir.createOutput("shorts", newIOContext(random()));
int num = TestUtil.nextInt(random(), 50, 3000);
short shorts[] = new short[num];
for (int i = 0; i < shorts.length; i++) {
shorts[i] = (short) random().nextInt();
output.writeShort(shorts[i]);
}
output.close();
// slice
IndexInput input = dir.openInput("shorts", newIOContext(random()));
RandomAccessInput slice = input.randomAccessSlice(0, input.length());
for (int i = 0; i < shorts.length; i++) {
assertEquals(shorts[i], slice.readShort(i * 2));
}
// subslices
for (int i = 1; i < shorts.length; i++) {
long offset = i * 2;
RandomAccessInput subslice = input.randomAccessSlice(offset, input.length() - offset);
for (int j = i; j < shorts.length; j++) {
assertEquals(shorts[j], subslice.readShort((j - i) * 2));
}
}
// with padding
for (int i = 0; i < 7; i++) {
String name = "shorts-" + i;
IndexOutput o = dir.createOutput(name, newIOContext(random()));
byte junk[] = new byte[i];
random().nextBytes(junk);
o.writeBytes(junk, junk.length);
input.seek(0);
o.copyBytes(input, input.length());
o.close();
IndexInput padded = dir.openInput(name, newIOContext(random()));
RandomAccessInput whole = padded.randomAccessSlice(i, padded.length() - i);
for (int j = 0; j < shorts.length; j++) {
assertEquals(shorts[j], whole.readShort(j * 2));
}
padded.close();
}
input.close();
}
}
public void testRandomByte() throws Exception {
try (Directory dir = getDirectory(createTempDir("testBytes"))) {
IndexOutput output = dir.createOutput("bytes", newIOContext(random()));
int num = TestUtil.nextInt(random(), 50, 3000);
byte bytes[] = new byte[num];
random().nextBytes(bytes);
for (int i = 0; i < bytes.length; i++) {
output.writeByte(bytes[i]);
}
output.close();
// slice
IndexInput input = dir.openInput("bytes", newIOContext(random()));
RandomAccessInput slice = input.randomAccessSlice(0, input.length());
for (int i = 0; i < bytes.length; i++) {
assertEquals(bytes[i], slice.readByte(i));
}
// subslices
for (int i = 1; i < bytes.length; i++) {
long offset = i;
RandomAccessInput subslice = input.randomAccessSlice(offset, input.length() - offset);
for (int j = i; j < bytes.length; j++) {
assertEquals(bytes[j], subslice.readByte(j - i));
}
}
// with padding
for (int i = 0; i < 7; i++) {
String name = "bytes-" + i;
IndexOutput o = dir.createOutput(name, newIOContext(random()));
byte junk[] = new byte[i];
random().nextBytes(junk);
o.writeBytes(junk, junk.length);
input.seek(0);
o.copyBytes(input, input.length());
o.close();
IndexInput padded = dir.openInput(name, newIOContext(random()));
RandomAccessInput whole = padded.randomAccessSlice(i, padded.length() - i);
for (int j = 0; j < bytes.length; j++) {
assertEquals(bytes[j], whole.readByte(j));
}
padded.close();
}
input.close();
}
}
/** try to stress slices of slices */
public void testSliceOfSlice() throws Exception {
try (Directory dir = getDirectory(createTempDir("sliceOfSlice"))) {
IndexOutput output = dir.createOutput("bytes", newIOContext(random()));
final int num;
if (TEST_NIGHTLY) {
num = TestUtil.nextInt(random(), 250, 2500);
} else {
num = TestUtil.nextInt(random(), 50, 250);
}
byte bytes[] = new byte[num];
random().nextBytes(bytes);
for (int i = 0; i < bytes.length; i++) {
output.writeByte(bytes[i]);
}
output.close();
IndexInput input = dir.openInput("bytes", newIOContext(random()));
// seek to a random spot shouldnt impact slicing.
input.seek(TestUtil.nextLong(random(), 0, input.length()));
for (int i = 0; i < num; i += 16) {
IndexInput slice1 = input.slice("slice1", i, num - i);
assertEquals(0, slice1.getFilePointer());
assertEquals(num - i, slice1.length());
// seek to a random spot shouldnt impact slicing.
slice1.seek(TestUtil.nextLong(random(), 0, slice1.length()));
for (int j = 0; j < slice1.length(); j += 16) {
IndexInput slice2 = slice1.slice("slice2", j, num - i - j);
assertEquals(0, slice2.getFilePointer());
assertEquals(num - i - j, slice2.length());
byte data[] = new byte[num];
System.arraycopy(bytes, 0, data, 0, i + j);
if (random().nextBoolean()) {
// read the bytes for this slice-of-slice
slice2.readBytes(data, i + j, num - i - j);
} else {
// seek to a random spot in between, read some, seek back and read the rest
long seek = TestUtil.nextLong(random(), 0, slice2.length());
slice2.seek(seek);
slice2.readBytes(data, (int) (i + j + seek), (int) (num - i - j - seek));
slice2.seek(0);
slice2.readBytes(data, i + j, (int) seek);
}
assertArrayEquals(bytes, data);
}
}
input.close();
}
}
/**
* This test that writes larger than the size of the buffer output
* will correctly increment the file pointer.
*/
public void testLargeWrites() throws IOException {
try (Directory dir = getDirectory(createTempDir("largeWrites"))) {
IndexOutput os = dir.createOutput("testBufferStart.txt", newIOContext(random()));
byte[] largeBuf = new byte[2048];
random().nextBytes(largeBuf);
long currentPos = os.getFilePointer();
os.writeBytes(largeBuf, largeBuf.length);
try {
assertEquals(currentPos + largeBuf.length, os.getFilePointer());
} finally {
os.close();
}
}
}
// LUCENE-6084
public void testIndexOutputToString() throws Throwable {
try (Directory dir = getDirectory(createTempDir())) {
IndexOutput out = dir.createOutput("camelCase.txt", newIOContext(random()));
assertTrue(out.toString(), out.toString().contains("camelCase.txt"));
out.close();
}
}
public void testDoubleCloseOutput() throws Throwable {
try (Directory dir = getDirectory(createTempDir())) {
IndexOutput out = dir.createOutput("foobar", newIOContext(random()));
out.writeString("testing");
out.close();
out.close(); // close again
}
}
public void testDoubleCloseInput() throws Throwable {
try (Directory dir = getDirectory(createTempDir())) {
IndexOutput out = dir.createOutput("foobar", newIOContext(random()));
out.writeString("testing");
out.close();
IndexInput in = dir.openInput("foobar", newIOContext(random()));
assertEquals("testing", in.readString());
in.close();
in.close(); // close again
}
}
public void testCreateTempOutput() throws Throwable {
try (Directory dir = getDirectory(createTempDir())) {
List<String> names = new ArrayList<>();
int iters = atLeast(50);
for (int iter = 0; iter < iters; iter++) {
IndexOutput out = dir.createTempOutput("foo", "bar", newIOContext(random()));
names.add(out.getName());
out.writeVInt(iter);
out.close();
}
for (int iter = 0; iter < iters; iter++) {
IndexInput in = dir.openInput(names.get(iter), newIOContext(random()));
assertEquals(iter, in.readVInt());
in.close();
}
Set<String> files = Arrays.stream(dir.listAll())
.filter(file -> !ExtrasFS.isExtra(file)) // remove any ExtrasFS stuff.
.collect(Collectors.toSet());
assertEquals(new HashSet<String>(names), files);
}
}
public void testCreateOutputForExistingFile() throws IOException {
try (Directory dir = getDirectory(createTempDir())) {
String name = "file";
try (IndexOutput out = dir.createOutput(name, IOContext.DEFAULT)) {
assert out != null;
}
// Try to create an existing file should fail.
expectThrows(FileAlreadyExistsException.class, () -> {
try (IndexOutput out = dir.createOutput(name, IOContext.DEFAULT)) {
assert out != null;
}
});
// Delete file and try to recreate it.
dir.deleteFile(name);
dir.createOutput(name, IOContext.DEFAULT).close();
}
}
public void testSeekToEndOfFile() throws IOException {
try (Directory dir = getDirectory(createTempDir())) {
try (IndexOutput out = dir.createOutput("a", IOContext.DEFAULT)) {
for (int i = 0; i < 1024; ++i) {
out.writeByte((byte) 0);
}
}
try (IndexInput in = dir.openInput("a", IOContext.DEFAULT)) {
in.seek(100);
assertEquals(100, in.getFilePointer());
in.seek(1024);
assertEquals(1024, in.getFilePointer());
}
}
}
public void testSeekBeyondEndOfFile() throws IOException {
try (Directory dir = getDirectory(createTempDir())) {
try (IndexOutput out = dir.createOutput("a", IOContext.DEFAULT)) {
for (int i = 0; i < 1024; ++i) {
out.writeByte((byte) 0);
}
}
try (IndexInput in = dir.openInput("a", IOContext.DEFAULT)) {
in.seek(100);
assertEquals(100, in.getFilePointer());
expectThrows(EOFException.class, () -> {
in.seek(1025);
});
}
}
}
// Make sure the FSDirectory impl properly "emulates" deletions on filesystems (Windows) with buggy deleteFile:
public void testPendingDeletions() throws IOException {
try (Directory dir = getDirectory(addVirusChecker(createTempDir()))) {
assumeTrue("we can only install VirusCheckingFS on an FSDirectory", dir instanceof FSDirectory);
FSDirectory fsDir = (FSDirectory) dir;
// Keep trying until virus checker refuses to delete:
final String fileName;
while (true) {
// create a random filename (segment file name style), so it cannot hit windows problem with special filenames ("con", "com1",...):
String candidate = IndexFileNames.segmentFileName(TestUtil.randomSimpleString(random(), 1, 6), TestUtil.randomSimpleString(random()), "test");
try (IndexOutput out = dir.createOutput(candidate, IOContext.DEFAULT)) {
out.getFilePointer(); // just fake access to prevent compiler warning
}
fsDir.deleteFile(candidate);
if (fsDir.getPendingDeletions().size() > 0) {
// good: virus checker struck and prevented deletion of fileName
fileName = candidate;
break;
}
}
// Make sure listAll does NOT include the file:
assertFalse(Arrays.asList(fsDir.listAll()).contains(fileName));
// Make sure fileLength claims it's deleted:
expectThrows(NoSuchFileException.class, () -> {
fsDir.fileLength(fileName);
});
// Make sure rename fails:
expectThrows(NoSuchFileException.class, () -> {
fsDir.rename(fileName, "file2");
});
// Make sure delete fails:
expectThrows(NoSuchFileException.class, () -> {
fsDir.deleteFile(fileName);
});
// Make sure we cannot open it for reading:
expectThrows(NoSuchFileException.class, () -> {
fsDir.openInput(fileName, IOContext.DEFAULT);
});
}
}
public void testListAllIsSorted() throws IOException {
try (Directory dir = getDirectory(createTempDir())) {
int count = atLeast(20);
Set<String> names = new HashSet<>();
while (names.size() < count) {
// create a random filename (segment file name style), so it cannot hit windows problem with special filenames ("con", "com1",...):
String name = IndexFileNames.segmentFileName(TestUtil.randomSimpleString(random(), 1, 6), TestUtil.randomSimpleString(random()), "test");
if (random().nextInt(5) == 1) {
IndexOutput out = dir.createTempOutput(name, "foo", IOContext.DEFAULT);
names.add(out.getName());
out.close();
} else if (names.contains(name) == false) {
IndexOutput out = dir.createOutput(name, IOContext.DEFAULT);
names.add(out.getName());
out.close();
}
}
String[] actual = dir.listAll();
String[] expected = actual.clone();
Arrays.sort(expected);
assertArrayEquals(expected, actual);
}
}
}