public void HistoryAddOfParentWithChangedChildExpectsOnlyAdd()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree originalTree = head;

            ProjectTree child, grandchild;

            child      = this.NewTree("child");
            child      = child.AddChildren(grandchild = this.NewTree("grand-child"));
            grandchild = child.Find(grandchild.Identity);
            child      = child.ReplaceDescendent(grandchild, grandchild.WithVisible(false));

            treeRevisions.Add(head = head.AddChildren(child)); // 1

            // Also change the grandchild again
            grandchild             = head.Find(grandchild.Identity);
            treeRevisions.Add(head = head.ReplaceDescendent(grandchild, grandchild.WithCapabilities("sc"))); // 2

            // Verify that up to when the subtree was added, only one change is reported.
            var differences = ProjectTree.GetDelta(originalTree, treeRevisions[1]).ToList();

            Assert.Single(differences);
            Assert.Equal(ChangeKind.Added, differences[0].Kind);
            Assert.Equal(differences[0].Identity, child.Identity); // Expected the removed node to be the child node

            // Verify that from beginning to very end, still only one change is reported.
            differences = ProjectTree.GetDelta(originalTree, head).ToList();
            Assert.Single(differences);
            Assert.Equal(ChangeKind.Added, differences[0].Kind);
            Assert.Equal(differences[0].Identity, child.Identity); // Expected the removed node to be the child node
        }
        public void HistoryIncludesOnlyTopLevelRemovals()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree child, grandchild1, grandchild2;

            treeRevisions.Add(head = head.AddChildren(child = this.NewTree("child")));
            treeRevisions.Add(head = head.AddDescendent(grandchild1 = this.NewTree("grand-child1"), child));
            treeRevisions.Add(head = head.ReplaceDescendent(grandchild1, grandchild2 = grandchild1.WithCaption("grand-child2")));

            // Add a sub-tree all at once to the tree.
            var greatX2grandChild1 = this.NewTree("great-great-grand-child1");
            var greatX2grandChild2 = this.NewTree("great-great-grand-child2");

            treeRevisions.Add(head = head.AddDescendent(this.NewTree("great-grand-child", children: new ProjectTree[] { greatX2grandChild1, greatX2grandChild2 }), grandchild1));

            // Now delete one of those discretly added nodes. (the idea here is to do our best to generate a funky history)
            treeRevisions.Add(head = head.RemoveDescendent(greatX2grandChild2));

            // And finally remove the top-level child node.
            treeRevisions.Add(head = head.RemoveDescendent(child));

            var differences = ProjectTree.GetDelta((ProjectTree)treeRevisions.First(), (ProjectTree)treeRevisions.Last()).ToList();

            Assert.Empty(differences);

            differences = ProjectTree.GetDelta((ProjectTree)treeRevisions[3], (ProjectTree)treeRevisions.Last()).ToList();
            Assert.Single(differences);
            Assert.Equal(ChangeKind.Removed, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.None, differences[0].Changes);
            Assert.Equal(differences[0].Identity, child.Identity); // The added node in history should be identity-equal to the most recent node.
        }
        public void HistoryRemovalOfParentWithChangedChildExpectOnlyRemove()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);

            ProjectTree child, grandchild;

            treeRevisions.Add(head = head.AddChildren(child = this.NewTree("child")));
            treeRevisions.Add(head = head.AddDescendent(grandchild = this.NewTree("grand-child"), child));
            ProjectTree originalTree = head;

            grandchild             = head.Find(grandchild.Identity);
            treeRevisions.Add(head = head.ReplaceDescendent(grandchild, grandchild.WithVisible(false)));

            child = head.Find(child.Identity);
            treeRevisions.Add(head = head.RemoveChildren(child));

            var differences = ProjectTree.GetDelta(originalTree, head).ToList();

            Assert.Single(differences);
            Assert.Equal(ChangeKind.Removed, differences[0].Kind);
            Assert.Equal(differences[0].Identity, child.Identity); // Expected the removed node to be the child node
        }
        public void HistoryOmitsAddedThenRemovedItems()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree node1a, node1b;

            treeRevisions.Add(head = head.AddChildren(node1a = this.NewTree("node1a")));
            treeRevisions.Add(head = head.ReplaceDescendent(node1a, node1b = node1a.WithCaption("node1b")));
            treeRevisions.Add(head = head.RemoveDescendent(node1b));

            var differences = ProjectTree.GetDelta((ProjectTree)treeRevisions.First(), (ProjectTree)treeRevisions.Last()).ToList();

            Assert.Empty(differences);
        }
        public void HistoryIncludesChangesToNodeProperties()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree node1a, node1b;

            treeRevisions.Add(head = head.AddChildren(node1a = this.NewTree("node1a")));
            treeRevisions.Add(head = head.ReplaceDescendent(node1a, node1b = node1a.WithCaption("node1b")));

            var differences = ProjectTree.GetDelta((ProjectTree)treeRevisions[1], (ProjectTree)treeRevisions.Last()).ToList();

            Assert.Single(differences);
            Assert.Equal(ChangeKind.Replaced, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.Caption, differences[0].Changes);
        }
        public void HistoryOmitsChangesAfterAdd()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree node1a, node1b;

            treeRevisions.Add(head = head.AddChildren(node1a = this.NewTree("node1a")));
            treeRevisions.Add(head = head.ReplaceDescendent(node1a, node1b = node1a.WithCapabilities("node1b")));

            var differences = ProjectTree.GetDelta((ProjectTree)treeRevisions.First(), (ProjectTree)treeRevisions.Last()).ToList();

            Assert.Single(differences);
            Assert.Equal(ChangeKind.Added, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.None, differences[0].Changes);
            Assert.Equal(differences[0].Identity, node1b.Identity); // The added node in history should be identity-equal to the most recent node.
        }
        public void HistoryIncludesOnlyTopLevelAddsWhenDescendentsChanged()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree child, grandchild;

            treeRevisions.Add(head = head.AddChildren(child = this.NewTree("child")));
            treeRevisions.Add(head = head.AddDescendent(grandchild = this.NewTree("grand-child"), child));
            grandchild             = head.Find(grandchild.Identity);
            treeRevisions.Add(head = head.ReplaceDescendent(grandchild, grandchild.WithVisible(false)));

            // Verify that from beginning to very end, still only one change is reported.
            var differences = ProjectTree.GetDelta(treeRevisions[0], treeRevisions.Last()).ToList();

            Assert.Single(differences);
            Assert.Equal(ChangeKind.Added, differences[0].Kind);
            Assert.Equal(differences[0].Identity, child.Identity); // Expected the removed node to be the child node
        }
        public void HistoryClaimsAtMostOneChangePerPropertyPerNode()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree node1a, node1b, node1c, node1d;

            treeRevisions.Add(head = head.AddChildren(node1a = this.NewTree("node1")));
            treeRevisions.Add(head = head.ReplaceDescendent(node1a, node1b = node1a.With("node1", visible: false)));
            treeRevisions.Add(head = head.ReplaceDescendent(node1b, node1c = node1b.With("node1", visible: false).WithCapabilities(ProjectTreeCapabilities.IncludeInProjectCandidate)));
            treeRevisions.Add(head = head.ReplaceDescendent(node1c, node1d = node1c.With("node1", visible: false).WithCapabilities(ProjectTreeCapabilities.IncludeInProjectCandidate, ProjectTreeCapabilities.SourceFile)));

            var differences = ProjectTree.GetDelta(treeRevisions[1], treeRevisions.Last()).ToList();

            // The point of this test is however to verify that if a given node changes multiple times along a tree's history
            // that each changed property is only reported once, rather than once each time the node changes at all.
            Assert.Single(differences);
            Assert.Equal(ChangeKind.Replaced, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.Visible | ProjectTreeChangedProperties.Capabilities, differences[0].Changes);
        }
        public void HistoryOmitsSelfCancelingPropertyChanges()
        {
            var head          = this.NewTree("some tree");
            var treeRevisions = new List <ProjectTree>();

            treeRevisions.Add(head);
            ProjectTree node1a, node1b, node1c, node1d;

            treeRevisions.Add(head = head.AddChildren(node1a = this.NewTree("node1")));
            treeRevisions.Add(head = head.ReplaceDescendent(node1a, node1b = node1a.With("node1", visible: false)));
            treeRevisions.Add(head = head.ReplaceDescendent(node1b, node1c = node1b.With("node1", visible: true)));
            treeRevisions.Add(head = head.ReplaceDescendent(node1c, node1d = node1c.With("node1", visible: false).WithCapabilities(ProjectTreeCapabilities.IncludeInProjectCandidate)));

            // span the visible: true -> false change
            var differences = ProjectTree.GetDelta(treeRevisions[1], treeRevisions[2]).ToList();

            Assert.Single(differences);
            Assert.Equal(ChangeKind.Replaced, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.Visible, differences[0].Changes);

            // span the visible: true -> false -> true change
            differences = ProjectTree.GetDelta(treeRevisions[1], treeRevisions[3]).ToList();
            Assert.Empty(differences);

            // span the visible: true -> false+capabilities change.
            differences = ProjectTree.GetDelta(treeRevisions[3], treeRevisions[4]).ToList();
            Assert.Single(differences);
            Assert.Equal(ChangeKind.Replaced, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.Capabilities | ProjectTreeChangedProperties.Visible, differences[0].Changes);

            // span the visible: false -> true -> false+capabilities change.
            differences = ProjectTree.GetDelta(treeRevisions[2], treeRevisions[4]).ToList();
            Assert.Single(differences);
            Assert.Equal(ChangeKind.Replaced, differences[0].Kind);
            Assert.Equal(ProjectTreeChangedProperties.Capabilities, differences[0].Changes);
        }