gt:         final IRepository repo = new TestRepository("123");
context: package de.setsoftware.reviewtool.model.changestructure;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertTrue;
  
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.Set;
  
  import org.junit.Test;
  
  import de.setsoftware.reviewtool.diffalgorithms.DiffAlgorithmFactory;
  import de.setsoftware.reviewtool.model.api.IFileHistoryEdge;
  import de.setsoftware.reviewtool.model.api.IFileHistoryNode;
  import de.setsoftware.reviewtool.model.api.IRepository;
  import de.setsoftware.reviewtool.model.api.IRevisionedFile;
  
  /**
   * Tests for {@link FileHistoryGraph}.
   */
  public final class FileHistoryGraphTest {
  
      private static FileHistoryGraph graph() {
          return new FileHistoryGraph(DiffAlgorithmFactory.createDefault());
      }
  
      private static FileHistoryEdge createAlphaNode(
              final IRepository repo,
              final FileHistoryGraph g,
              final ProxyableFileHistoryNode node) {
          final ProxyableFileHistoryNode alphaNode = g.getNodeFor(
                  ChangestructureFactory.createFileInRevision(node.getFile().getPath(),
                          ChangestructureFactory.createUnknownRevision(repo)));
          final FileHistoryEdge alphaEdge = new FileHistoryEdge(g, alphaNode, node, IFileHistoryEdge.Type.NORMAL);
          return alphaEdge;
      }
  
      @Test
      public void testAdditionOfFile() {
          final IRepository repo = new TestRepository("123");
          final FileHistoryGraph g = graph();
  
          final IRevisionedFile aRev =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 2L));
          g.addAddition(aRev.getPath(), aRev.getRevision());
  
          final ProxyableFileHistoryNode aNode = g.getNodeFor(aRev);
          assertEquals(aRev, aNode.getFile());
          assertEquals(IFileHistoryNode.Type.ADDED, aNode.getType());
          assertEquals(false, aNode.isCopyTarget());
  
          assertEquals(Collections.singleton(createAlphaNode(repo, g, aNode)), aNode.getAncestors());
          assertEquals(Collections.emptySet(), aNode.getDescendants());
      }
  
      @Test
      public void testDeletionOfKnownFile() {
          final IRepository repo = new TestRepository("123");
          final FileHistoryGraph g = graph();
  
          final IRevisionedFile aRevPrev =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 1L));
  
          g.addAddition(aRevPrev.getPath(), aRevPrev.getRevision());
  
          final IRevisionedFile aRevDel =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 2L));
  
          g.addDeletion(aRevDel.getPath(), aRevDel.getRevision());
  
          final ProxyableFileHistoryNode aNode = g.getNodeFor(aRevPrev);
          assertEquals(aRevPrev, aNode.getFile());
          assertEquals(IFileHistoryNode.Type.ADDED, aNode.getType());
          assertEquals(false, aNode.isCopyTarget());
          assertEquals(Collections.singleton(createAlphaNode(repo, g, aNode)), aNode.getAncestors());
  
          final ProxyableFileHistoryNode aNodeDel = g.getNodeFor(aRevDel);
          assertEquals(aRevDel, aNodeDel.getFile());
          assertEquals(IFileHistoryNode.Type.DELETED, aNodeDel.getType());
          assertEquals(false, aNodeDel.isCopyTarget());
          assertEquals(Collections.emptySet(), aNodeDel.getDescendants());
  
          final FileHistoryEdge aEdge = new FileHistoryEdge(g, aNode, aNodeDel, IFileHistoryEdge.Type.NORMAL);
          assertEquals(Collections.singleton(aEdge), aNode.getDescendants());
          assertEquals(Collections.singleton(aEdge), aNodeDel.getAncestors());
      }
  
      @Test
      public void testDeletionOfUnknownFile() {
          final IRepository repo = new TestRepository("123");
          final FileHistoryGraph g = graph();
  
          final IRevisionedFile aRevPrev =
                  ChangestructureFactory.createFileInRevision("/trunk/a",
                          ChangestructureFactory.createUnknownRevision(repo));
          final IRevisionedFile aRevDel =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 2L));
  
          g.addDeletion(aRevDel.getPath(), aRevDel.getRevision());
  
          final ProxyableFileHistoryNode aNodeDel = g.getNodeFor(aRevDel);
          assertEquals(aRevDel, aNodeDel.getFile());
          assertEquals(IFileHistoryNode.Type.DELETED, aNodeDel.getType());
          assertEquals(false, aNodeDel.isCopyTarget());
          assertEquals(Collections.emptySet(), aNodeDel.getDescendants());
  
          final ProxyableFileHistoryNode aNodePrev = g.getNodeFor(aRevPrev);
          assertEquals(aRevPrev, aNodePrev.getFile());
          assertEquals(IFileHistoryNode.Type.UNCONFIRMED, aNodePrev.getType());
          assertEquals(false, aNodePrev.isCopyTarget());
          assertEquals(Collections.emptySet(), aNodePrev.getAncestors());
  
          final FileHistoryEdge aEdge = new FileHistoryEdge(g, aNodePrev, aNodeDel, IFileHistoryEdge.Type.NORMAL);
          assertEquals(Collections.singleton(aEdge), aNodePrev.getDescendants());
          assertEquals(Collections.singleton(aEdge), aNodeDel.getAncestors());
      }
  
      @Test
      public void testDeletionOfKnownMovedFile() {
          final IRepository repo = new TestRepository("123");
          final FileHistoryGraph g = graph();
  
          final IRevisionedFile xRev =
                  ChangestructureFactory.createFileInRevision("/trunk/a/x", new TestRepoRevision(repo, 1L));
  
          g.addAddition(xRev.getPath(), xRev.getRevision());
  
          final IRevisionedFile xRevDel =
                  ChangestructureFactory.createFileInRevision(xRev.getPath(), new TestRepoRevision(repo, 2L));
          final IRevisionedFile yRev =
                  ChangestructureFactory.createFileInRevision("/trunk/a/y", new TestRepoRevision(repo, 2L));
  
          g.addCopy(xRev.getPath(), xRev.getRevision(), yRev.getPath(), yRev.getRevision());
          g.addDeletion(xRevDel.getPath(), xRevDel.getRevision());
  
          final IRevisionedFile yRevDel =
                  ChangestructureFactory.createFileInRevision(yRev.getPath(), new TestRepoRevision(repo, 3L));
  
          g.addDeletion(yRevDel.getPath(), yRevDel.getRevision());
  
          // revision 1
          final ProxyableFileHistoryNode xNode = g.getNodeFor(xRev);
          assertEquals(xRev, xNode.getFile());
          assertEquals(IFileHistoryNode.Type.ADDED, xNode.getType());
          assertEquals(false, xNode.isCopyTarget());
          assertEquals(Collections.singleton(createAlphaNode(repo, g, xNode)), xNode.getAncestors());
  
          // revision 2
          final ProxyableFileHistoryNode xNodeDel = g.getNodeFor(xRevDel);
          assertEquals(xRevDel, xNodeDel.getFile());
          assertEquals(IFileHistoryNode.Type.DELETED, xNodeDel.getType());
          assertEquals(false, xNodeDel.isCopyTarget());
          assertEquals(Collections.emptySet(), xNodeDel.getDescendants());
  
          final ProxyableFileHistoryNode yNode = g.getNodeFor(yRev);
          assertEquals(yRev, yNode.getFile());
          assertEquals(IFileHistoryNode.Type.CHANGED, yNode.getType());
          assertEquals(true, yNode.isCopyTarget());
  
          final FileHistoryEdge xEdgeDel = new FileHistoryEdge(g, xNode, xNodeDel, IFileHistoryEdge.Type.NORMAL);
          final FileHistoryEdge yEdge = new FileHistoryEdge(g, xNode, yNode, IFileHistoryEdge.Type.COPY);
          assertEquals(new HashSet<>(Arrays.asList(xEdgeDel, yEdge)), xNode.getDescendants());
          assertEquals(Collections.singleton(xEdgeDel), xNodeDel.getAncestors());
          assertEquals(Collections.singleton(yEdge), yNode.getAncestors());
  
          // revision 3
          final ProxyableFileHistoryNode yNodeDel = g.getNodeFor(yRevDel);
          assertEquals(yRevDel, yNodeDel.getFile());
          assertEquals(IFileHistoryNode.Type.DELETED, yNodeDel.getType());
          assertEquals(false, yNodeDel.isCopyTarget());
  
          final FileHistoryEdge yEdgeCopyDel = new FileHistoryEdge(g, yNode, yNodeDel,
                  IFileHistoryEdge.Type.NORMAL);
          assertEquals(Collections.singleton(yEdgeCopyDel), yNodeDel.getAncestors());
          assertEquals(Collections.singleton(yEdgeCopyDel), yNode.getDescendants());
      }
  
      @Test
      public void testReplacementOfKnownFile() {
          final IRepository repo = new TestRepository("123");
          final FileHistoryGraph g = graph();
  
          final IRevisionedFile aRevPrev =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 1L));
  
          g.addAddition(aRevPrev.getPath(), aRevPrev.getRevision());
  
          final IRevisionedFile aRevReplaced =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 2L));
  
          g.addDeletion(aRevReplaced.getPath(), aRevReplaced.getRevision());
          g.addAddition(aRevReplaced.getPath(), aRevReplaced.getRevision());
  
          final ProxyableFileHistoryNode aNode = g.getNodeFor(aRevPrev);
          assertEquals(aRevPrev, aNode.getFile());
          assertEquals(IFileHistoryNode.Type.ADDED, aNode.getType());
          assertEquals(false, aNode.isCopyTarget());
          assertEquals(Collections.singleton(createAlphaNode(repo, g, aNode)), aNode.getAncestors());
  
          final ProxyableFileHistoryNode aNodeReplaced = g.getNodeFor(aRevReplaced);
          assertEquals(aRevReplaced, aNodeReplaced.getFile());
          assertEquals(IFileHistoryNode.Type.REPLACED, aNodeReplaced.getType());
          assertEquals(false, aNodeReplaced.isCopyTarget());
          assertEquals(Collections.emptySet(), aNodeReplaced.getDescendants());
  
          final FileHistoryEdge aEdge = new FileHistoryEdge(g, aNode, aNodeReplaced, IFileHistoryEdge.Type.NORMAL);
          assertEquals(Collections.singleton(aEdge), aNode.getDescendants());
          assertEquals(Collections.singleton(aEdge), aNodeReplaced.getAncestors());
      }
  
      @Test
      public void testReplacementOfUnknownFile() {
          final IRepository repo = new TestRepository("123");
          final FileHistoryGraph g = graph();
  
          final IRevisionedFile aRevPrev =
                  ChangestructureFactory.createFileInRevision("/trunk/a",
                          ChangestructureFactory.createUnknownRevision(repo));
          final IRevisionedFile aRevReplaced =
                  ChangestructureFactory.createFileInRevision("/trunk/a", new TestRepoRevision(repo, 2L));
  
          g.addDeletion(aRevReplaced.getPath(), aRevReplaced.getRevision());
          g.addAddition(aRevReplaced.getPath(), aRevReplaced.getRevision());
  
          final ProxyableFileHistoryNode aNodePrev = g.getNodeFor(aRevPrev);
          assertEquals(aRevPrev, aNodePrev.getFile());
          assertEquals(IFileHistoryNode.Type.UNCONFIRMED, aNodePrev.getType());
          assertEquals(false, aNodePrev.isCopyTarget());
          assertEquals(Collections.emptySet(), aNodePrev.getAncestors());
  
          final ProxyableFileHistoryNode aNodeReplaced = g.getNodeFor(aRevReplaced);
          assertEquals(aRevReplaced, aNodeReplaced.getFile());
          assertEquals(IFileHistoryNode.Type.REPLACED, aNodeReplaced.getType());
          assertEquals(false, aNodeReplaced.isCopyTarget());
          assertEquals(Collections.emptySet(), aNodeReplaced.getDescendants());
  
          final FileHistoryEdge aEdge = new FileHistoryEdge(g, aNodePrev, aNodeReplaced, IFileHistoryEdge.Type.NORMAL);
          assertEquals(Collections.singleton(aEdge), aNodePrev.getDescendants());
          assertEquals(Collections.singleton(aEdge), aNodeReplaced.getAncestors());
      }
  
      @Test
      public void testAdditionAndDeletionOfSameFileInSubsequentRevisions() {
  