public void RunMergingVisitor_RemapsLogicalLocations()
        {
            var baselineRun = new Run
            {
                Tool = new Tool {
                    Driver = new ToolComponent {
                        Name = "Test Tool"
                    }
                },
                LogicalLocations = new List <LogicalLocation>
                {
                    new LogicalLocation
                    {
                        Index              = 0,
                        ParentIndex        = -1,
                        Kind               = LogicalLocationKind.Namespace,
                        Name               = "N1",
                        FullyQualifiedName = "N1"
                    },

                    new LogicalLocation
                    {
                        Index              = 1,
                        ParentIndex        = 0,
                        Kind               = LogicalLocationKind.Type,
                        Name               = "T1",
                        FullyQualifiedName = "N1.T1"
                    },

                    new LogicalLocation
                    {
                        Index              = 2,
                        ParentIndex        = 1,
                        Kind               = LogicalLocationKind.Member,
                        Name               = "M1",
                        FullyQualifiedName = "N1.T1.M1"
                    },
                },

                Results = new List <Result>
                {
                    new Result
                    {
                        Locations = new List <Location>
                        {
                            new Location
                            {
                                LogicalLocation = new LogicalLocation
                                {
                                    Index = 2
                                }
                            }
                        }
                    },

                    // This result implicates a logical location that has already been encountered.
                    // The visitor should count it only once.
                    new Result
                    {
                        Locations = new List <Location>
                        {
                            new Location
                            {
                                LogicalLocation = new LogicalLocation
                                {
                                    Index = 1
                                }
                            }
                        }
                    }
                }
            };

            // This run has a single result that points to a different logical location.
            var currentRun = new Run
            {
                Tool = new Tool {
                    Driver = new ToolComponent {
                        Name = "Test Tool"
                    }
                },
                LogicalLocations = new List <LogicalLocation>
                {
                    new LogicalLocation
                    {
                        Index              = 0,
                        ParentIndex        = -1,
                        Kind               = LogicalLocationKind.Namespace,
                        Name               = "N2",
                        FullyQualifiedName = "N2"
                    },

                    new LogicalLocation
                    {
                        Index              = 1,
                        ParentIndex        = 0,
                        Kind               = LogicalLocationKind.Type,
                        Name               = "T2",
                        FullyQualifiedName = "N2.T2"
                    }
                },

                Results = new List <Result>
                {
                    new Result
                    {
                        Locations = new List <Location>
                        {
                            new Location
                            {
                                LogicalLocation = new LogicalLocation
                                {
                                    Index = 1
                                }
                            }
                        }
                    }
                }
            };

            Run mergedRun       = currentRun.DeepClone();
            Run baselineRunCopy = baselineRun.DeepClone();

            // Merge Results from the Current and Baseline Run
            var visitor = new RunMergingVisitor();

            visitor.VisitRun(mergedRun);
            visitor.VisitRun(baselineRunCopy);

            visitor.PopulateWithMerged(mergedRun);

            // Verify each Result points to a LogicalLocation with the same FullyQualifiedName as before
            VerifyLogicalLocationMatches(mergedRun, currentRun, 0);
            VerifyLogicalLocationMatches(mergedRun, baselineRun, currentRun.Results.Count);

            // The logical locations in the merged run are the union of the logical locations from
            // the baseline and current runs. In this test case, there are no logical locations in
            // common between the two runs, so the number of logical locations in the merged run is
            // just the sum of the baseline and current runs.
            mergedRun.LogicalLocations.Count.Should().Be(baselineRun.LogicalLocations.Count + currentRun.LogicalLocations.Count);
        }
        public void RunMergingVisitor_RemapsNestedFilesProperly()
        {
            // This run has a single result that points to a doubly nested file.
            var baselineRun = new Run
            {
                Tool = new Tool {
                    Driver = new ToolComponent {
                        Name = "Test Tool"
                    }
                },
                Artifacts = new List <Artifact>
                {
                    new Artifact {
                        Location = new ArtifactLocation {
                            Index = 0
                        }, Contents = new ArtifactContent {
                            Text = "1"
                        }
                    },
                    new Artifact {
                        Location = new ArtifactLocation {
                            Index = 1
                        }, Contents = new ArtifactContent {
                            Text = "1.2"
                        }, ParentIndex = 0
                    },
                    new Artifact {
                        Location = new ArtifactLocation {
                            Index = 2
                        }, Contents = new ArtifactContent {
                            Text = "1.2.3."
                        }, ParentIndex = 1
                    }
                },
                Results = new List <Result>
                {
                    new Result
                    {
                        Locations = new List <Location>
                        {
                            new Location
                            {
                                PhysicalLocation = new PhysicalLocation
                                {
                                    ArtifactLocation = new ArtifactLocation
                                    {
                                        Index = 2
                                    }
                                }
                            }
                        }
                    }
                }
            };

            // This run has a single result pointing to a single file location.
            var currentRun = new Run
            {
                Tool = new Tool {
                    Driver = new ToolComponent {
                        Name = "Test Tool"
                    }
                },
                Artifacts = new List <Artifact>
                {
                    new Artifact {
                        Location = new ArtifactLocation {
                            Index = 0
                        }, Contents = new ArtifactContent {
                            Text = "New"
                        }
                    },
                    new Artifact {
                        Location = new ArtifactLocation {
                            Index = 1
                        }, Contents = new ArtifactContent {
                            Text = "Child of new"
                        }, ParentIndex = 0
                    },
                },
                Results = new List <Result>
                {
                    new Result
                    {
                        Locations = new List <Location>
                        {
                            new Location
                            {
                                PhysicalLocation = new PhysicalLocation
                                {
                                    ArtifactLocation = new ArtifactLocation
                                    {
                                        Index = 0
                                    }
                                }
                            }
                        }
                    }
                }
            };

            // Use the RunMergingVisitor to merge two runs
            var visitor = new RunMergingVisitor();

            Run mergedRun       = currentRun.DeepClone();
            Run baselineRunCopy = baselineRun.DeepClone();

            visitor.VisitRun(mergedRun);
            visitor.VisitRun(baselineRunCopy);

            visitor.PopulateWithMerged(mergedRun);

            // Confirm that Artifacts (including indirectly referenced ones) were all copied to the destination Run
            mergedRun.Artifacts.Count.Should().Be(baselineRun.Artifacts.Count + currentRun.Artifacts.Count);

            // Confirm that Artifacts have consistent indices in the merged Run
            for (int i = 0; i < mergedRun.Artifacts.Count; i++)
            {
                mergedRun.Artifacts[i].Location.Index.Should().Be(i);
            }

            // Verify each merged Run Result ArtifactIndex points to an Artifact with the same
            // text as the corresponding original Result
            VerifyArtifactMatches(mergedRun, currentRun, 0);
            VerifyArtifactMatches(mergedRun, baselineRun, currentRun.Results.Count);

            // Verify that the parent index chain for the nested artifacts was updated properly
            // Recall that visitor placed the artifacts from the baseline run at the end of the
            // merged run's artifacts array, and that in this test case, the artifacts in the
            // baseline run were arranged with the most nested artifact at the end.
            Artifact artifact = mergedRun.Artifacts[mergedRun.Artifacts.Count - 1];

            artifact.ParentIndex.Should().Be(3);
            artifact = mergedRun.Artifacts[artifact.ParentIndex];

            artifact.ParentIndex.Should().Be(2);
            artifact = mergedRun.Artifacts[artifact.ParentIndex];

            artifact.ParentIndex.Should().Be(-1);
        }