|
@@ -18,7 +18,10 @@
|
|
|
package org.apache.hadoop.fs;
|
|
|
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.util.StringUtils;
|
|
|
import java.io.*;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Random;
|
|
|
|
|
|
import junit.framework.*;
|
|
|
|
|
@@ -28,6 +31,7 @@ import junit.framework.*;
|
|
|
public class TestLocalFileSystem extends TestCase {
|
|
|
private static String TEST_ROOT_DIR
|
|
|
= System.getProperty("test.build.data","build/test/data/work-dir/localfs");
|
|
|
+ private final Path TEST_PATH = new Path(TEST_ROOT_DIR, "test-file");
|
|
|
|
|
|
|
|
|
static void writeFile(FileSystem fs, Path name) throws IOException {
|
|
@@ -203,4 +207,72 @@ public class TestLocalFileSystem extends TestCase {
|
|
|
new RawLocalFileSystem().new LocalFSFileInputStream(path), 1024);
|
|
|
assertNotNull(bis.getFileDescriptor());
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Regression test for HADOOP-9307: BufferedFSInputStream returning
|
|
|
+ * wrong results after certain sequences of seeks and reads.
|
|
|
+ */
|
|
|
+ public void testBufferedFSInputStream() throws IOException {
|
|
|
+ Configuration conf = new Configuration();
|
|
|
+ conf.setClass("fs.file.impl", RawLocalFileSystem.class, FileSystem.class);
|
|
|
+ conf.set("fs.file.impl.disable.cache", "true");
|
|
|
+ conf.setInt("io.file.buffer.size", 4096);
|
|
|
+ FileSystem fs = FileSystem.get(conf);
|
|
|
+
|
|
|
+ byte[] buf = new byte[10*1024];
|
|
|
+ new Random().nextBytes(buf);
|
|
|
+
|
|
|
+ // Write random bytes to file
|
|
|
+ FSDataOutputStream stream = fs.create(TEST_PATH);
|
|
|
+ try {
|
|
|
+ stream.write(buf);
|
|
|
+ } finally {
|
|
|
+ stream.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ Random r = new Random();
|
|
|
+
|
|
|
+ FSDataInputStream stm = fs.open(TEST_PATH);
|
|
|
+ // Record the sequence of seeks and reads which trigger a failure.
|
|
|
+ int seeks[] = new int[10];
|
|
|
+ int reads[] = new int[10];
|
|
|
+ try {
|
|
|
+ for (int i = 0; i < 1000; i++) {
|
|
|
+ int seekOff = r.nextInt(buf.length);
|
|
|
+ int toRead = r.nextInt(Math.min(buf.length - seekOff, 32000));
|
|
|
+
|
|
|
+ seeks[i % seeks.length] = seekOff;
|
|
|
+ reads[i % reads.length] = toRead;
|
|
|
+ verifyRead(stm, buf, seekOff, toRead);
|
|
|
+
|
|
|
+ }
|
|
|
+ } catch (AssertionError afe) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("Sequence of actions:\n");
|
|
|
+ for (int j = 0; j < seeks.length; j++) {
|
|
|
+ sb.append("seek @ ").append(seeks[j]).append(" ")
|
|
|
+ .append("read ").append(reads[j]).append("\n");
|
|
|
+ }
|
|
|
+ System.err.println(sb.toString());
|
|
|
+ throw afe;
|
|
|
+ } finally {
|
|
|
+ stm.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void verifyRead(FSDataInputStream stm, byte[] fileContents,
|
|
|
+ int seekOff, int toRead) throws IOException {
|
|
|
+ byte[] out = new byte[toRead];
|
|
|
+ stm.seek(seekOff);
|
|
|
+ stm.readFully(out);
|
|
|
+ byte[] expected = Arrays.copyOfRange(fileContents, seekOff, seekOff+toRead);
|
|
|
+ if (!Arrays.equals(out, expected)) {
|
|
|
+ String s ="\nExpected: " +
|
|
|
+ StringUtils.byteToHexString(expected) +
|
|
|
+ "\ngot: " +
|
|
|
+ StringUtils.byteToHexString(out) +
|
|
|
+ "\noff=" + seekOff + " len=" + toRead;
|
|
|
+ fail(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|