|
@@ -20,27 +20,36 @@ package org.apache.hadoop.fs.contract;
|
|
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
import java.io.IOException;
|
|
|
+import java.util.List;
|
|
|
|
|
|
import org.apache.hadoop.fs.FileStatus;
|
|
|
import org.apache.hadoop.fs.FileSystem;
|
|
|
+import org.apache.hadoop.fs.FilterFileSystem;
|
|
|
+import org.apache.hadoop.fs.LocatedFileStatus;
|
|
|
import org.apache.hadoop.fs.Path;
|
|
|
+import org.apache.hadoop.fs.PathFilter;
|
|
|
+import org.apache.hadoop.fs.RemoteIterator;
|
|
|
import org.junit.Test;
|
|
|
-import static org.junit.Assert.assertEquals;
|
|
|
-import static org.junit.Assert.assertTrue;
|
|
|
-import org.slf4j.Logger;
|
|
|
-import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import static org.apache.hadoop.fs.contract.ContractTestUtils.*;
|
|
|
|
|
|
/**
|
|
|
- * Test getFileStatus -if supported
|
|
|
+ * Test getFileStatus and related listing operations.
|
|
|
*/
|
|
|
public abstract class AbstractContractGetFileStatusTest extends
|
|
|
AbstractFSContractTestBase {
|
|
|
- private static final Logger LOG =
|
|
|
- LoggerFactory.getLogger(AbstractContractGetFileStatusTest.class);
|
|
|
|
|
|
private Path testPath;
|
|
|
private Path target;
|
|
|
|
|
|
+ // the tree parameters. Kept small to avoid killing object store test
|
|
|
+ // runs too much.
|
|
|
+
|
|
|
+ private static final int TREE_DEPTH = 2;
|
|
|
+ private static final int TREE_WIDTH = 3;
|
|
|
+ private static final int TREE_FILES = 4;
|
|
|
+ private static final int TREE_FILESIZE = 512;
|
|
|
+
|
|
|
@Override
|
|
|
public void setup() throws Exception {
|
|
|
super.setup();
|
|
@@ -56,7 +65,7 @@ public abstract class AbstractContractGetFileStatusTest extends
|
|
|
try {
|
|
|
FileStatus status = getFileSystem().getFileStatus(target);
|
|
|
//got here: trouble
|
|
|
- fail("expected a failure");
|
|
|
+ fail("expected a failure, got " + status);
|
|
|
} catch (FileNotFoundException e) {
|
|
|
//expected
|
|
|
handleExpectedException(e);
|
|
@@ -65,20 +74,561 @@ public abstract class AbstractContractGetFileStatusTest extends
|
|
|
|
|
|
@Test
|
|
|
public void testListStatusEmptyDirectory() throws IOException {
|
|
|
- // remove the test directory
|
|
|
+ describe("List status on an empty directory");
|
|
|
+ Path subfolder = createDirWithEmptySubFolder();
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ Path path = getContract().getTestPath();
|
|
|
+ new TreeScanResults(fs.listStatus(path))
|
|
|
+ .assertSizeEquals("listStatus(" + path + ")", 0, 1, 0);
|
|
|
+ describe("Test on empty subdirectory");
|
|
|
+ new TreeScanResults(fs.listStatus(subfolder))
|
|
|
+ .assertSizeEquals("listStatus(empty subfolder)", 0, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListFilesEmptyDirectoryNonrecursive() throws IOException {
|
|
|
+ listFilesOnEmptyDir(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListFilesEmptyDirectoryRecursive() throws IOException {
|
|
|
+ listFilesOnEmptyDir(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Call listFiles on an directory with an empty subdir.
|
|
|
+ * @param recursive should the list be recursive?
|
|
|
+ * @throws IOException IO Problems
|
|
|
+ */
|
|
|
+ private void listFilesOnEmptyDir(boolean recursive) throws IOException {
|
|
|
+ describe("Invoke listFiles(recursive=" + recursive + ")" +
|
|
|
+ " on empty directories, expect nothing found");
|
|
|
+ Path subfolder = createDirWithEmptySubFolder();
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ new TreeScanResults(fs.listFiles(getContract().getTestPath(), recursive))
|
|
|
+ .assertSizeEquals("listFiles(test dir, " + recursive + ")", 0, 0, 0);
|
|
|
+ describe("Test on empty subdirectory");
|
|
|
+ new TreeScanResults(fs.listFiles(subfolder, recursive))
|
|
|
+ .assertSizeEquals("listFiles(empty subfolder, " + recursive + ")",
|
|
|
+ 0, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListLocatedStatusEmptyDirectory() throws IOException {
|
|
|
+ describe("Invoke listLocatedStatus() on empty directories;" +
|
|
|
+ " expect directories to be found");
|
|
|
+ Path subfolder = createDirWithEmptySubFolder();
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ new TreeScanResults(fs.listLocatedStatus(getContract().getTestPath()))
|
|
|
+ .assertSizeEquals("listLocatedStatus(test dir)", 0, 1, 0);
|
|
|
+ describe("Test on empty subdirectory");
|
|
|
+ new TreeScanResults(fs.listLocatedStatus(subfolder))
|
|
|
+ .assertSizeEquals("listLocatedStatus(empty subfolder)", 0, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * All tests cases against complex directories are aggregated into one, so
|
|
|
+ * that the setup and teardown costs against object stores can be shared.
|
|
|
+ * @throws Throwable
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testComplexDirActions() throws Throwable {
|
|
|
+ TreeScanResults tree = createTestTree();
|
|
|
+ checkListStatusStatusComplexDir(tree);
|
|
|
+ checkListLocatedStatusStatusComplexDir(tree);
|
|
|
+ checkListFilesComplexDirNonRecursive(tree);
|
|
|
+ checkListFilesComplexDirRecursive(tree);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link FileSystem#listStatus(Path)} on a complex
|
|
|
+ * directory tree.
|
|
|
+ * @param tree directory tree to list.
|
|
|
+ * @throws Throwable
|
|
|
+ */
|
|
|
+ protected void checkListStatusStatusComplexDir(TreeScanResults tree)
|
|
|
+ throws Throwable {
|
|
|
+ describe("Expect listStatus to list all entries in top dir only");
|
|
|
+
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ TreeScanResults listing = new TreeScanResults(
|
|
|
+ fs.listStatus(tree.getBasePath()));
|
|
|
+ listing.assertSizeEquals("listStatus()", TREE_FILES, TREE_WIDTH, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link FileSystem#listLocatedStatus(Path)} on a complex
|
|
|
+ * directory tree.
|
|
|
+ * @param tree directory tree to list.
|
|
|
+ * @throws Throwable
|
|
|
+ */
|
|
|
+ protected void checkListLocatedStatusStatusComplexDir(TreeScanResults tree)
|
|
|
+ throws Throwable {
|
|
|
+ describe("Expect listLocatedStatus to list all entries in top dir only");
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ TreeScanResults listing = new TreeScanResults(
|
|
|
+ fs.listLocatedStatus(tree.getBasePath()));
|
|
|
+ listing.assertSizeEquals("listLocatedStatus()", TREE_FILES, TREE_WIDTH, 0);
|
|
|
+ verifyFileStats(fs.listLocatedStatus(tree.getBasePath()));
|
|
|
+
|
|
|
+ // listLocatedStatus and listStatus must return the same files.
|
|
|
+ TreeScanResults listStatus = new TreeScanResults(
|
|
|
+ fs.listStatus(tree.getBasePath()));
|
|
|
+ listing.assertEquivalent(listStatus);
|
|
|
+
|
|
|
+ // now check without using
|
|
|
+ List<LocatedFileStatus> statusThroughNext = toListThroughNextCallsAlone(
|
|
|
+ fs.listLocatedStatus(tree.getBasePath())
|
|
|
+ );
|
|
|
+ TreeScanResults resultsThroughNext = new TreeScanResults(statusThroughNext);
|
|
|
+ listStatus.assertFieldsEquivalent("files", listing,
|
|
|
+ listStatus.getFiles(),
|
|
|
+ resultsThroughNext.getFiles());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link FileSystem#listFiles(Path, boolean)} on a complex
|
|
|
+ * directory tree and the recursive flag set to false.
|
|
|
+ * @param tree directory tree to list.
|
|
|
+ * @throws Throwable
|
|
|
+ */
|
|
|
+ protected void checkListFilesComplexDirNonRecursive(TreeScanResults tree)
|
|
|
+ throws Throwable {
|
|
|
+ describe("Expect non-recursive listFiles(false) to list all entries" +
|
|
|
+ " in top dir only");
|
|
|
FileSystem fs = getFileSystem();
|
|
|
- assertTrue(fs.delete(getContract().getTestPath(), true));
|
|
|
+ TreeScanResults listing = new TreeScanResults(
|
|
|
+ fs.listFiles(tree.getBasePath(), false));
|
|
|
+ listing.assertSizeEquals("listFiles(false)", TREE_FILES, 0, 0);
|
|
|
+ verifyFileStats(fs.listFiles(tree.getBasePath(), false));
|
|
|
+
|
|
|
+ // the files listed should match the set of files in a listStatus() call.
|
|
|
+ // the directories are not checked
|
|
|
+ TreeScanResults listStatus = new TreeScanResults(
|
|
|
+ fs.listStatus(tree.getBasePath()));
|
|
|
+ listStatus.assertFieldsEquivalent("files", listing,
|
|
|
+ listStatus.getFiles(),
|
|
|
+ listing.getFiles());
|
|
|
+ List<LocatedFileStatus> statusThroughNext = toListThroughNextCallsAlone(
|
|
|
+ fs.listFiles(tree.getBasePath(), false));
|
|
|
+ TreeScanResults resultsThroughNext = new TreeScanResults(statusThroughNext);
|
|
|
+ listStatus.assertFieldsEquivalent("files", listing,
|
|
|
+ listStatus.getFiles(),
|
|
|
+ resultsThroughNext.getFiles());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link FileSystem#listFiles(Path, boolean)} on a complex
|
|
|
+ * directory tree and the recursive flag set to true.
|
|
|
+ * @param tree directory tree to list.
|
|
|
+ * @throws Throwable
|
|
|
+ */
|
|
|
+ protected void checkListFilesComplexDirRecursive(TreeScanResults tree)
|
|
|
+ throws Throwable {
|
|
|
+ describe("Expect recursive listFiles(true) to" +
|
|
|
+ " list all files down the tree");
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ TreeScanResults listing = new TreeScanResults(
|
|
|
+ fs.listFiles(tree.getBasePath(), true));
|
|
|
+ // files are checked, but not the directories.
|
|
|
+ tree.assertFieldsEquivalent("files", listing, tree.getFiles(),
|
|
|
+ listing.getFiles());
|
|
|
+ int count = verifyFileStats(fs.listFiles(tree.getBasePath(), true));
|
|
|
+ // assert that the content matches that of a tree walk
|
|
|
+ describe("verifying consistency with treewalk's files");
|
|
|
+ TreeScanResults treeWalk = treeWalk(fs, tree.getBasePath());
|
|
|
+ treeWalk.assertFieldsEquivalent("files", listing,
|
|
|
+ treeWalk.getFiles(),
|
|
|
+ listing.getFiles());
|
|
|
+ assertEquals("Size of status list through next() calls",
|
|
|
+ count,
|
|
|
+ toListThroughNextCallsAlone(
|
|
|
+ fs.listFiles(tree.getBasePath(), true)).size());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListFilesNoDir() throws Throwable {
|
|
|
+ describe("test the listFiles calls on a path which is not present");
|
|
|
+ Path path = path("missing");
|
|
|
+ try {
|
|
|
+ RemoteIterator<LocatedFileStatus> iterator
|
|
|
+ = getFileSystem().listFiles(path, false);
|
|
|
+ fail("Expected an exception, got an iterator: " + iterator);
|
|
|
+ } catch (FileNotFoundException expected) {
|
|
|
+ // expected
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ RemoteIterator<LocatedFileStatus> iterator
|
|
|
+ = getFileSystem().listFiles(path, true);
|
|
|
+ fail("Expected an exception, got an iterator: " + iterator);
|
|
|
+ } catch (FileNotFoundException expected) {
|
|
|
+ // expected
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testLocatedStatusNoDir() throws Throwable {
|
|
|
+ describe("test the LocatedStatus call on a path which is not present");
|
|
|
+ try {
|
|
|
+ RemoteIterator<LocatedFileStatus> iterator
|
|
|
+ = getFileSystem().listLocatedStatus(path("missing"));
|
|
|
+ fail("Expected an exception, got an iterator: " + iterator);
|
|
|
+ } catch (FileNotFoundException expected) {
|
|
|
+ // expected
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListStatusNoDir() throws Throwable {
|
|
|
+ describe("test the listStatus(path) call on a path which is not present");
|
|
|
+ try {
|
|
|
+ getFileSystem().listStatus(path("missing"));
|
|
|
+ fail("Expected an exception");
|
|
|
+ } catch (FileNotFoundException expected) {
|
|
|
+ // expected
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListStatusFilteredNoDir() throws Throwable {
|
|
|
+ describe("test the listStatus(path, filter) call on a missing path");
|
|
|
+ try {
|
|
|
+ getFileSystem().listStatus(path("missing"), ALL_PATHS);
|
|
|
+ fail("Expected an exception");
|
|
|
+ } catch (FileNotFoundException expected) {
|
|
|
+ // expected
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListStatusFilteredFile() throws Throwable {
|
|
|
+ describe("test the listStatus(path, filter) on a file");
|
|
|
+ Path f = touchf("liststatus");
|
|
|
+ assertEquals(0, getFileSystem().listStatus(f, NO_PATHS).length);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListStatusFile() throws Throwable {
|
|
|
+ describe("test the listStatus(path) on a file");
|
|
|
+ Path f = touchf("liststatusfile");
|
|
|
+ verifyStatusArrayMatchesFile(f, getFileSystem().listStatus(f));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListFilesFile() throws Throwable {
|
|
|
+ describe("test the listStatus(path) on a file");
|
|
|
+ Path f = touchf("listfilesfile");
|
|
|
+ List<LocatedFileStatus> statusList = toList(
|
|
|
+ getFileSystem().listFiles(f, false));
|
|
|
+ assertEquals("size of file list returned", 1, statusList.size());
|
|
|
+ assertIsNamedFile(f, statusList.get(0));
|
|
|
+ List<LocatedFileStatus> statusList2 = toListThroughNextCallsAlone(
|
|
|
+ getFileSystem().listFiles(f, false));
|
|
|
+ assertEquals("size of file list returned through next() calls",
|
|
|
+ 1, statusList2.size());
|
|
|
+ assertIsNamedFile(f, statusList2.get(0));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListFilesFileRecursive() throws Throwable {
|
|
|
+ describe("test the listFiles(path, true) on a file");
|
|
|
+ Path f = touchf("listfilesRecursive");
|
|
|
+ List<LocatedFileStatus> statusList = toList(
|
|
|
+ getFileSystem().listFiles(f, true));
|
|
|
+ assertEquals("size of file list returned", 1, statusList.size());
|
|
|
+ assertIsNamedFile(f, statusList.get(0));
|
|
|
+ List<LocatedFileStatus> statusList2 = toListThroughNextCallsAlone(
|
|
|
+ getFileSystem().listFiles(f, true));
|
|
|
+ assertEquals("size of file list returned", 1, statusList2.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListLocatedStatusFile() throws Throwable {
|
|
|
+ describe("test the listLocatedStatus(path) on a file");
|
|
|
+ Path f = touchf("listLocatedStatus");
|
|
|
+ List<LocatedFileStatus> statusList = toList(
|
|
|
+ getFileSystem().listLocatedStatus(f));
|
|
|
+ assertEquals("size of file list returned", 1, statusList.size());
|
|
|
+ assertIsNamedFile(f, statusList.get(0));
|
|
|
+ List<LocatedFileStatus> statusList2 = toListThroughNextCallsAlone(
|
|
|
+ getFileSystem().listLocatedStatus(f));
|
|
|
+ assertEquals("size of file list returned through next() calls",
|
|
|
+ 1, statusList2.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Verify a returned status array matches a single named file.
|
|
|
+ * @param f filename
|
|
|
+ * @param status status array
|
|
|
+ */
|
|
|
+ private void verifyStatusArrayMatchesFile(Path f, FileStatus[] status) {
|
|
|
+ assertEquals(1, status.length);
|
|
|
+ FileStatus fileStatus = status[0];
|
|
|
+ assertIsNamedFile(f, fileStatus);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Verify that a file status refers to a file at the given path.
|
|
|
+ * @param f filename
|
|
|
+ * @param fileStatus status to validate
|
|
|
+ */
|
|
|
+ private void assertIsNamedFile(Path f, FileStatus fileStatus) {
|
|
|
+ assertEquals("Wrong pathname in " + fileStatus, f, fileStatus.getPath());
|
|
|
+ assertTrue("Not a file: " + fileStatus, fileStatus.isFile());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Touch a file with a given name; return the path.
|
|
|
+ * @param name name
|
|
|
+ * @return the full name
|
|
|
+ * @throws IOException IO Problems
|
|
|
+ */
|
|
|
+ Path touchf(String name) throws IOException {
|
|
|
+ Path path = path(name);
|
|
|
+ ContractTestUtils.touch(getFileSystem(), path);
|
|
|
+ return path;
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * Clear the test directory and add an empty subfolder.
|
|
|
+ * @return the path to the subdirectory
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
+ private Path createDirWithEmptySubFolder() throws IOException {
|
|
|
+ // remove the test directory
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+ Path path = getContract().getTestPath();
|
|
|
+ fs.delete(path, true);
|
|
|
// create a - non-qualified - Path for a subdir
|
|
|
- Path subfolder = getContract().getTestPath().suffix("/"+testPath.getName());
|
|
|
- assertTrue(fs.mkdirs(subfolder));
|
|
|
+ Path subfolder = path.suffix('/' + this.methodName.getMethodName());
|
|
|
+ mkdirs(subfolder);
|
|
|
+ return subfolder;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Create a test tree.
|
|
|
+ * @return the details about the created tree. The files and directories
|
|
|
+ * are those created under the path, not the base directory created.
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
+ private TreeScanResults createTestTree() throws IOException {
|
|
|
+ return createSubdirs(getFileSystem(), path(methodName.getMethodName()),
|
|
|
+ TREE_DEPTH, TREE_WIDTH, TREE_FILES, TREE_FILESIZE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Scan through a filestatus iterator, get the status of every element and
|
|
|
+ * verify core attributes. This should identify a situation where the
|
|
|
+ * attributes of a file/dir retrieved in a listing operation do not
|
|
|
+ * match the values individually retrieved. That is: the metadata returned
|
|
|
+ * in a directory listing is different from the explicitly retrieved data.
|
|
|
+ *
|
|
|
+ * Timestamps are not compared.
|
|
|
+ * @param results iterator to scan
|
|
|
+ * @return the number of entries in the result set
|
|
|
+ * @throws IOException any IO problem
|
|
|
+ */
|
|
|
+ private int verifyFileStats(RemoteIterator<LocatedFileStatus> results)
|
|
|
+ throws IOException {
|
|
|
+ describe("verifying file statuses");
|
|
|
+ int count = 0;
|
|
|
+ while (results.hasNext()) {
|
|
|
+ count++;
|
|
|
+ LocatedFileStatus next = results.next();
|
|
|
+ FileStatus fileStatus = getFileSystem().getFileStatus(next.getPath());
|
|
|
+ assertEquals("isDirectory", fileStatus.isDirectory(), next.isDirectory());
|
|
|
+ assertEquals("isFile", fileStatus.isFile(), next.isFile());
|
|
|
+ assertEquals("getLen", fileStatus.getLen(), next.getLen());
|
|
|
+ assertEquals("getOwner", fileStatus.getOwner(), next.getOwner());
|
|
|
+ }
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testListStatusFiltering() throws Throwable {
|
|
|
+ describe("Call listStatus() against paths and directories with filtering");
|
|
|
+ Path file1 = touchf("file-1.txt");
|
|
|
+ touchf("file-2.txt");
|
|
|
+ Path parent = file1.getParent();
|
|
|
+ FileStatus[] result;
|
|
|
|
|
|
- // assert empty ls on the empty dir
|
|
|
- assertEquals("ls on an empty directory not of length 0", 0,
|
|
|
- fs.listStatus(subfolder).length);
|
|
|
+ verifyListStatus(0, parent, NO_PATHS);
|
|
|
+ verifyListStatus(2, parent, ALL_PATHS);
|
|
|
+
|
|
|
+ MatchesNameFilter file1Filter = new MatchesNameFilter("file-1.txt");
|
|
|
+ result = verifyListStatus(1, parent, file1Filter);
|
|
|
+ assertEquals(file1, result[0].getPath());
|
|
|
+
|
|
|
+ verifyListStatus(0, file1, NO_PATHS);
|
|
|
+ result = verifyListStatus(1, file1, ALL_PATHS);
|
|
|
+ assertEquals(file1, result[0].getPath());
|
|
|
+ result = verifyListStatus(1, file1, file1Filter);
|
|
|
+ assertEquals(file1, result[0].getPath());
|
|
|
+
|
|
|
+ // empty subdirectory
|
|
|
+ Path subdir = path("subdir");
|
|
|
+ mkdirs(subdir);
|
|
|
+ verifyListStatus(0, subdir, NO_PATHS);
|
|
|
+ verifyListStatus(0, subdir, ALL_PATHS);
|
|
|
+ verifyListStatus(0, subdir, new MatchesNameFilter("subdir"));
|
|
|
+ }
|
|
|
|
|
|
- // assert non-empty ls on parent dir
|
|
|
- assertTrue("ls on a non-empty directory of length 0",
|
|
|
- fs.listStatus(getContract().getTestPath()).length > 0);
|
|
|
+ @Test
|
|
|
+ public void testListLocatedStatusFiltering() throws Throwable {
|
|
|
+ describe("Call listLocatedStatus() with filtering");
|
|
|
+ describe("Call listStatus() against paths and directories with filtering");
|
|
|
+ Path file1 = touchf("file-1.txt");
|
|
|
+ Path file2 = touchf("file-2.txt");
|
|
|
+ Path parent = file1.getParent();
|
|
|
+ FileSystem fs = getFileSystem();
|
|
|
+
|
|
|
+ touch(fs, file1);
|
|
|
+ touch(fs, file2);
|
|
|
+ // this is not closed: ignore any IDE warnings.
|
|
|
+ ExtendedFilterFS xfs = new ExtendedFilterFS(fs);
|
|
|
+ List<LocatedFileStatus> result;
|
|
|
+
|
|
|
+ verifyListStatus(0, parent, NO_PATHS);
|
|
|
+ verifyListStatus(2, parent, ALL_PATHS);
|
|
|
+
|
|
|
+ MatchesNameFilter file1Filter = new MatchesNameFilter("file-1.txt");
|
|
|
+ result = verifyListLocatedStatus(xfs, 1, parent, file1Filter);
|
|
|
+ assertEquals(file1, result.get(0).getPath());
|
|
|
+
|
|
|
+ verifyListLocatedStatus(xfs, 0, file1, NO_PATHS);
|
|
|
+ verifyListLocatedStatus(xfs, 1, file1, ALL_PATHS);
|
|
|
+ assertEquals(file1, result.get(0).getPath());
|
|
|
+ verifyListLocatedStatus(xfs, 1, file1, file1Filter);
|
|
|
+ assertEquals(file1, result.get(0).getPath());
|
|
|
+ verifyListLocatedStatusNextCalls(xfs, 1, file1, file1Filter);
|
|
|
+
|
|
|
+ // empty subdirectory
|
|
|
+ Path subdir = path("subdir");
|
|
|
+ mkdirs(subdir);
|
|
|
+ verifyListLocatedStatus(xfs, 0, subdir, NO_PATHS);
|
|
|
+ verifyListLocatedStatus(xfs, 0, subdir, ALL_PATHS);
|
|
|
+ verifyListLocatedStatusNextCalls(xfs, 0, subdir, ALL_PATHS);
|
|
|
+ verifyListLocatedStatus(xfs, 0, subdir, new MatchesNameFilter("subdir"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Execute {@link FileSystem#listStatus(Path, PathFilter)},
|
|
|
+ * verify the length of the result, then return the listing.
|
|
|
+ * @param expected expected length
|
|
|
+ * @param path path to list
|
|
|
+ * @param filter filter to apply
|
|
|
+ * @return the listing
|
|
|
+ * @throws IOException IO Problems
|
|
|
+ */
|
|
|
+ private FileStatus[] verifyListStatus(int expected,
|
|
|
+ Path path,
|
|
|
+ PathFilter filter) throws IOException {
|
|
|
+ FileStatus[] result = getFileSystem().listStatus(path, filter);
|
|
|
+ assertEquals("length of listStatus(" + path + ", " + filter + " )",
|
|
|
+ expected, result.length);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Execute {@link FileSystem#listLocatedStatus(Path, PathFilter)},
|
|
|
+ * generate a list from the iterator, verify the length of the list returned
|
|
|
+ * and then return it.
|
|
|
+ * @param expected expected length
|
|
|
+ * @param path path to list
|
|
|
+ * @param filter filter to apply
|
|
|
+ * @return the listing
|
|
|
+ * @throws IOException IO Problems
|
|
|
+ */
|
|
|
+ private List<LocatedFileStatus> verifyListLocatedStatus(ExtendedFilterFS xfs,
|
|
|
+ int expected,
|
|
|
+ Path path,
|
|
|
+ PathFilter filter) throws IOException {
|
|
|
+ RemoteIterator<LocatedFileStatus> it = xfs.listLocatedStatus(path, filter);
|
|
|
+ List<LocatedFileStatus> result = toList(it);
|
|
|
+ assertEquals("length of listLocatedStatus(" + path + ", " + filter + " )",
|
|
|
+ expected, result.size());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Execute {@link FileSystem#listLocatedStatus(Path, PathFilter)},
|
|
|
+ * generate a list from the iterator, verify the length of the list returned
|
|
|
+ * and then return it.
|
|
|
+ * Uses {@link ContractTestUtils#toListThroughNextCallsAlone(RemoteIterator)}
|
|
|
+ * to stress the iteration process.
|
|
|
+ * @param expected expected length
|
|
|
+ * @param path path to list
|
|
|
+ * @param filter filter to apply
|
|
|
+ * @return the listing
|
|
|
+ * @throws IOException IO Problems
|
|
|
+ */
|
|
|
+ private List<LocatedFileStatus> verifyListLocatedStatusNextCalls(
|
|
|
+ ExtendedFilterFS xfs,
|
|
|
+ int expected,
|
|
|
+ Path path,
|
|
|
+ PathFilter filter) throws IOException {
|
|
|
+ RemoteIterator<LocatedFileStatus> it = xfs.listLocatedStatus(path, filter);
|
|
|
+ List<LocatedFileStatus> result = toListThroughNextCallsAlone(it);
|
|
|
+ assertEquals("length of listLocatedStatus(" + path + ", " + filter + " )",
|
|
|
+ expected, result.size());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final PathFilter ALL_PATHS = new AllPathsFilter();
|
|
|
+ private static final PathFilter NO_PATHS = new NoPathsFilter();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Accept everything.
|
|
|
+ */
|
|
|
+ private static final class AllPathsFilter implements PathFilter {
|
|
|
+ @Override
|
|
|
+ public boolean accept(Path path) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Accept nothing.
|
|
|
+ */
|
|
|
+ private static final class NoPathsFilter implements PathFilter {
|
|
|
+ @Override
|
|
|
+ public boolean accept(Path path) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Path filter which only expects paths whose final name element
|
|
|
+ * equals the {@code match} field.
|
|
|
+ */
|
|
|
+ private static final class MatchesNameFilter implements PathFilter {
|
|
|
+ private final String match;
|
|
|
+
|
|
|
+ MatchesNameFilter(String match) {
|
|
|
+ this.match = match;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean accept(Path path) {
|
|
|
+ return match.equals(path.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * A filesystem filter which exposes the protected method
|
|
|
+ * {@link #listLocatedStatus(Path, PathFilter)}.
|
|
|
+ */
|
|
|
+ protected static final class ExtendedFilterFS extends FilterFileSystem {
|
|
|
+ public ExtendedFilterFS(FileSystem fs) {
|
|
|
+ super(fs);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public RemoteIterator<LocatedFileStatus> listLocatedStatus(Path f,
|
|
|
+ PathFilter filter)
|
|
|
+ throws IOException {
|
|
|
+ return super.listLocatedStatus(f, filter);
|
|
|
+ }
|
|
|
}
|
|
|
}
|