static void assertSnapshot(
     INodesInPath inodesInPath, boolean isSnapshot, final Snapshot snapshot, int index) {
   assertEquals(isSnapshot, inodesInPath.isSnapshot());
   assertEquals(
       Snapshot.getSnapshotId(isSnapshot ? snapshot : null), inodesInPath.getPathSnapshotId());
   if (!isSnapshot) {
     assertEquals(Snapshot.getSnapshotId(snapshot), inodesInPath.getLatestSnapshotId());
   }
   if (isSnapshot && index >= 0) {
     assertEquals(Snapshot.Root.class, inodesInPath.getINode(index).getClass());
   }
 }
  /** for snapshot file while adding a new file after snapshot. */
  @Test(timeout = 15000)
  public void testSnapshotPathINodesWithAddedFile() throws Exception {
    // Create a snapshot for the dir, and check the inodes for the path
    // pointing to a snapshot file
    hdfs.allowSnapshot(sub1);
    hdfs.createSnapshot(sub1, "s4");

    // Add a new file /TestSnapshot/sub1/file3
    final Path file3 = new Path(sub1, "file3");
    DFSTestUtil.createFile(hdfs, file3, 1024, REPLICATION, seed);

    Snapshot s4;
    {
      // Check the inodes for /TestSnapshot/sub1/.snapshot/s4/file3
      String snapshotPath = sub1.toString() + "/.snapshot/s4/file3";
      String[] names = INode.getPathNames(snapshotPath);
      byte[][] components = INode.getPathComponents(names);
      INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
      // Length of inodes should be (components.length - 1), since we will ignore
      // ".snapshot"
      assertEquals(nodesInPath.length(), components.length - 1);
      // The number of non-null inodes should be components.length - 2, since
      // snapshot of file3 does not exist
      assertEquals(getNumNonNull(nodesInPath), components.length - 2);
      s4 = getSnapshot(nodesInPath, "s4", 3);

      // SnapshotRootIndex should still be 3: {root, Testsnapshot, sub1, s4, null}
      assertSnapshot(nodesInPath, true, s4, 3);

      // Check the last INode in inodes, which should be null
      assertNull(nodesInPath.getINode(nodesInPath.length() - 1));
    }

    // Check the inodes for /TestSnapshot/sub1/file3
    String[] names = INode.getPathNames(file3.toString());
    byte[][] components = INode.getPathComponents(names);
    INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // The number of inodes should be equal to components.length
    assertEquals(nodesInPath.length(), components.length);

    // The returned nodesInPath should be non-snapshot
    assertSnapshot(nodesInPath, false, s4, -1);

    // The last INode should be associated with file3
    assertEquals(nodesInPath.getINode(components.length - 1).getFullPathName(), file3.toString());
    assertEquals(nodesInPath.getINode(components.length - 2).getFullPathName(), sub1.toString());
    assertEquals(nodesInPath.getINode(components.length - 3).getFullPathName(), dir.toString());
    hdfs.deleteSnapshot(sub1, "s4");
    hdfs.disallowSnapshot(sub1);
  }
 static Snapshot getSnapshot(INodesInPath inodesInPath, String name, int index) {
   if (name == null) {
     return null;
   }
   final INode inode = inodesInPath.getINode(index - 1);
   return inode.asDirectory().getSnapshot(DFSUtil.string2Bytes(name));
 }
  /** for snapshot file after deleting the original file. */
  @Test(timeout = 15000)
  public void testSnapshotPathINodesAfterDeletion() throws Exception {
    // Create a snapshot for the dir, and check the inodes for the path
    // pointing to a snapshot file
    hdfs.allowSnapshot(sub1);
    hdfs.createSnapshot(sub1, "s2");

    // Delete the original file /TestSnapshot/sub1/file1
    hdfs.delete(file1, false);

    final Snapshot snapshot;
    {
      // Resolve the path for the snapshot file
      // /TestSnapshot/sub1/.snapshot/s2/file1
      String snapshotPath = sub1.toString() + "/.snapshot/s2/file1";
      String[] names = INode.getPathNames(snapshotPath);
      byte[][] components = INode.getPathComponents(names);
      INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
      // Length of inodes should be (components.length - 1), since we will ignore
      // ".snapshot"
      assertEquals(nodesInPath.length(), components.length - 1);
      // SnapshotRootIndex should be 3: {root, Testsnapshot, sub1, s2, file1}
      snapshot = getSnapshot(nodesInPath, "s2", 3);
      assertSnapshot(nodesInPath, true, snapshot, 3);

      // Check the INode for file1 (snapshot file)
      final INode inode = nodesInPath.getLastINode();
      assertEquals(file1.getName(), inode.getLocalName());
      assertTrue(inode.asFile().isWithSnapshot());
    }

    // Check the INodes for path /TestSnapshot/sub1/file1
    String[] names = INode.getPathNames(file1.toString());
    byte[][] components = INode.getPathComponents(names);
    INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // The length of inodes should be equal to components.length
    assertEquals(nodesInPath.length(), components.length);
    // The number of non-null elements should be components.length - 1 since
    // file1 has been deleted
    assertEquals(getNumNonNull(nodesInPath), components.length - 1);
    // The returned nodesInPath should be non-snapshot
    assertSnapshot(nodesInPath, false, snapshot, -1);
    // The last INode should be null, and the one before should be associated
    // with sub1
    assertNull(nodesInPath.getINode(components.length - 1));
    assertEquals(nodesInPath.getINode(components.length - 2).getFullPathName(), sub1.toString());
    assertEquals(nodesInPath.getINode(components.length - 3).getFullPathName(), dir.toString());
    hdfs.deleteSnapshot(sub1, "s2");
    hdfs.disallowSnapshot(sub1);
  }
 private int getNumNonNull(INodesInPath iip) {
   List<INode> inodes = iip.getReadOnlyINodes();
   for (int i = inodes.size() - 1; i >= 0; i--) {
     if (inodes.get(i) != null) {
       return i + 1;
     }
   }
   return 0;
 }
  /** for normal (non-snapshot) file. */
  @Test(timeout = 15000)
  public void testNonSnapshotPathINodes() throws Exception {
    // Get the inodes by resolving the path of a normal file
    String[] names = INode.getPathNames(file1.toString());
    byte[][] components = INode.getPathComponents(names);
    INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // The number of inodes should be equal to components.length
    assertEquals(nodesInPath.length(), components.length);
    // The returned nodesInPath should be non-snapshot
    assertSnapshot(nodesInPath, false, null, -1);

    // The last INode should be associated with file1
    assertTrue(
        "file1=" + file1 + ", nodesInPath=" + nodesInPath,
        nodesInPath.getINode(components.length - 1) != null);
    assertEquals(nodesInPath.getINode(components.length - 1).getFullPathName(), file1.toString());
    assertEquals(nodesInPath.getINode(components.length - 2).getFullPathName(), sub1.toString());
    assertEquals(nodesInPath.getINode(components.length - 3).getFullPathName(), dir.toString());

    nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    assertEquals(nodesInPath.length(), components.length);
    assertSnapshot(nodesInPath, false, null, -1);
    assertEquals(nodesInPath.getLastINode().getFullPathName(), file1.toString());
  }
  /** for snapshot file while modifying file after snapshot. */
  @Test(timeout = 15000)
  public void testSnapshotPathINodesAfterModification() throws Exception {
    // First check the INode for /TestSnapshot/sub1/file1
    String[] names = INode.getPathNames(file1.toString());
    byte[][] components = INode.getPathComponents(names);
    INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // The number of inodes should be equal to components.length
    assertEquals(nodesInPath.length(), components.length);

    // The last INode should be associated with file1
    assertEquals(nodesInPath.getINode(components.length - 1).getFullPathName(), file1.toString());
    // record the modification time of the inode
    final long modTime = nodesInPath.getINode(nodesInPath.length() - 1).getModificationTime();

    // Create a snapshot for the dir, and check the inodes for the path
    // pointing to a snapshot file
    hdfs.allowSnapshot(sub1);
    hdfs.createSnapshot(sub1, "s3");

    // Modify file1
    DFSTestUtil.appendFile(hdfs, file1, "the content for appending");

    // Check the INodes for snapshot of file1
    String snapshotPath = sub1.toString() + "/.snapshot/s3/file1";
    names = INode.getPathNames(snapshotPath);
    components = INode.getPathComponents(names);
    INodesInPath ssNodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // Length of ssInodes should be (components.length - 1), since we will
    // ignore ".snapshot"
    assertEquals(ssNodesInPath.length(), components.length - 1);
    final Snapshot s3 = getSnapshot(ssNodesInPath, "s3", 3);
    assertSnapshot(ssNodesInPath, true, s3, 3);
    // Check the INode for snapshot of file1
    INode snapshotFileNode = ssNodesInPath.getLastINode();
    assertEquals(snapshotFileNode.getLocalName(), file1.getName());
    assertTrue(snapshotFileNode.asFile().isWithSnapshot());
    // The modification time of the snapshot INode should be the same with the
    // original INode before modification
    assertEquals(modTime, snapshotFileNode.getModificationTime(ssNodesInPath.getPathSnapshotId()));

    // Check the INode for /TestSnapshot/sub1/file1 again
    names = INode.getPathNames(file1.toString());
    components = INode.getPathComponents(names);
    INodesInPath newNodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    assertSnapshot(newNodesInPath, false, s3, -1);
    // The number of inodes should be equal to components.length
    assertEquals(newNodesInPath.length(), components.length);
    // The last INode should be associated with file1
    final int last = components.length - 1;
    assertEquals(newNodesInPath.getINode(last).getFullPathName(), file1.toString());
    // The modification time of the INode for file3 should have been changed
    Assert.assertFalse(modTime == newNodesInPath.getINode(last).getModificationTime());
    hdfs.deleteSnapshot(sub1, "s3");
    hdfs.disallowSnapshot(sub1);
  }
  /** for snapshot file. */
  @Test(timeout = 15000)
  public void testSnapshotPathINodes() throws Exception {
    // Create a snapshot for the dir, and check the inodes for the path
    // pointing to a snapshot file
    hdfs.allowSnapshot(sub1);
    hdfs.createSnapshot(sub1, "s1");
    // The path when accessing the snapshot file of file1 is
    // /TestSnapshot/sub1/.snapshot/s1/file1
    String snapshotPath = sub1.toString() + "/.snapshot/s1/file1";
    String[] names = INode.getPathNames(snapshotPath);
    byte[][] components = INode.getPathComponents(names);
    INodesInPath nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // Length of inodes should be (components.length - 1), since we will ignore
    // ".snapshot"
    assertEquals(nodesInPath.length(), components.length - 1);
    // SnapshotRootIndex should be 3: {root, Testsnapshot, sub1, s1, file1}
    final Snapshot snapshot = getSnapshot(nodesInPath, "s1", 3);
    assertSnapshot(nodesInPath, true, snapshot, 3);
    // Check the INode for file1 (snapshot file)
    INode snapshotFileNode = nodesInPath.getLastINode();
    assertINodeFile(snapshotFileNode, file1);
    assertTrue(snapshotFileNode.getParent().isWithSnapshot());

    // Call getExistingPathINodes and request only one INode.
    nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    assertEquals(nodesInPath.length(), components.length - 1);
    assertSnapshot(nodesInPath, true, snapshot, 3);
    // Check the INode for file1 (snapshot file)
    assertINodeFile(nodesInPath.getLastINode(), file1);

    // Resolve the path "/TestSnapshot/sub1/.snapshot"
    String dotSnapshotPath = sub1.toString() + "/.snapshot";
    names = INode.getPathNames(dotSnapshotPath);
    components = INode.getPathComponents(names);
    nodesInPath = INodesInPath.resolve(fsdir.rootDir, components, false);
    // The number of INodes returned should still be components.length
    // since we put a null in the inode array for ".snapshot"
    assertEquals(nodesInPath.length(), components.length);

    // No SnapshotRoot dir is included in the resolved inodes
    assertSnapshot(nodesInPath, true, snapshot, -1);
    // The last INode should be null, the last but 1 should be sub1
    assertNull(nodesInPath.getLastINode());
    assertEquals(nodesInPath.getINode(-2).getFullPathName(), sub1.toString());
    assertTrue(nodesInPath.getINode(-2).isDirectory());

    String[] invalidPathComponent = {"invalidDir", "foo", ".snapshot", "bar"};
    Path invalidPath = new Path(invalidPathComponent[0]);
    for (int i = 1; i < invalidPathComponent.length; i++) {
      invalidPath = new Path(invalidPath, invalidPathComponent[i]);
      try {
        hdfs.getFileStatus(invalidPath);
        Assert.fail();
      } catch (FileNotFoundException fnfe) {
        System.out.println("The exception is expected: " + fnfe);
      }
    }
    hdfs.deleteSnapshot(sub1, "s1");
    hdfs.disallowSnapshot(sub1);
  }