public void RemapIndicesVisitor_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
                                    }
                                }
                            }
                        }
                    }
                }
            };

            Run mergedRun = currentRun.DeepClone();

            // How does this remapping work? First, we initialize the indices remapper
            // with a set of existing files (analagous to the files list of the most
            // recent log in a baselining situation). Next, visit a set of results
            // from the baseline itself, providing the historical set of file data
            // objects from the baseline. The visit does two things: 1) updates the
            // visitor files table with the superset of file data objects between
            // the baseline and current run, 2) updates individual results from the
            // baseline so that their file index values are correct.

            // 1. At this point the merged run consists of a copy of the current run.
            //    After visitor construction, we should see that the visitor's
            //    CurrentFiles property is equivalent to mergedRun.Files. The visitor
            //    has also been initialized with a dictionary that uses the complete
            //    file hierarchy as a key into the files array
            var visitor = new RemapIndicesVisitor(mergedRun.Artifacts, currentLogicalLocations: null);

            visitor.CurrentArtifacts.Should().BeEquivalentTo(mergedRun.Artifacts);
            visitor.RemappedArtifacts.Count.Should().Be(mergedRun.Artifacts.Count);


            // 2. We set HistoricalFiles to point to the old files array from the
            //    baseline run, then visit each baseline result. After each result
            //    visit, any file data objects (including parent files) for the
            //    result have been added to visitor.CurrentFiles, if missing. Each
            //    result fileIndex data is updated. We move the updated result
            //    objects to the merged run.

            // Various operations mutate state. We will make a copy so that we can
            // compare our ulitmate results to an unaltered original baseline.
            Run baselineRunCopy = baselineRun.DeepClone();

            visitor.HistoricalFiles = baselineRunCopy.Artifacts;
            foreach (Result result in baselineRunCopy.Results)
            {
                visitor.VisitResult(result);
                mergedRun.Results.Add(result);
            }

            // 3. After completing the results array visit, we'll grab the
            //    files array that represents all merged files from the runs.
            mergedRun.Artifacts = visitor.CurrentArtifacts;

            // The artifacts in the merged run are the union of the artifacts from the baseline and
            // current runs. In this test case, there are no artifacts in common between the two
            // runs, so the number of artifacts in the merged run is just the sum of the baseline
            // and current runs.
            mergedRun.Artifacts.Count.Should().Be(baselineRun.Artifacts.Count + currentRun.Artifacts.Count);

            // Every artifact in the merged run's artifacts array has an index that points to its
            // own location in the array, even though the artifacts that came from the baseline run
            // originally resided at a different index.
            for (int i = 0; i < mergedRun.Artifacts.Count; i++)
            {
                mergedRun.Artifacts[i].Location.Index.Should().Be(i);
            }

            // The artifacts from the current run are in the same place as before. The artifacts
            // from the baseline run have moved to the end of the merged array. Verify this by
            // matching up their text contents.
            for (int i = 0; i < currentRun.Artifacts.Count; i++)
            {
                int oldIndex = i;
                int newIndex = i;

                currentRun.Artifacts[oldIndex].Contents.Text.Should().Be(mergedRun.Artifacts[newIndex].Contents.Text);
            }

            for (int i = 0; i < baselineRun.Artifacts.Count; i++)
            {
                int oldIndex = i;
                int newIndex = i + currentRun.Artifacts.Count;

                baselineRun.Artifacts[oldIndex].Contents.Text.Should().Be(mergedRun.Artifacts[newIndex].Contents.Text);
            }

            // The artifact index in the merged run's results have been adjusted as well.
            for (int i = 0; i < currentRun.Results.Count; ++i)
            {
                int oldIndex = currentRun.Results[i].Locations[0].PhysicalLocation.ArtifactLocation.Index;
                int newIndex = mergedRun.Results[i].Locations[0].PhysicalLocation.ArtifactLocation.Index;

                mergedRun.Artifacts[newIndex].Contents.Text.Should().Be(currentRun.Artifacts[oldIndex].Contents.Text);
            }

            // 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);
        }
        public void RemapIndicesVisitor_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();

            var visitor = new RemapIndicesVisitor(currentArtifacts: null, currentLogicalLocations: mergedRun.LogicalLocations);

            visitor.CurrentLogicalLocations.Should().BeEquivalentTo(mergedRun.LogicalLocations);
            visitor.RemappedLogicalLocations.Count.Should().Be(mergedRun.LogicalLocations.Count);

            Run baselineRunCopy = baselineRun.DeepClone();

            visitor.HistoricalLogicalLocations = baselineRunCopy.LogicalLocations;
            foreach (Result result in baselineRunCopy.Results)
            {
                visitor.VisitResult(result);
                mergedRun.Results.Add(result);
            }

            mergedRun.LogicalLocations = visitor.CurrentLogicalLocations;

            // 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.
            visitor.CurrentLogicalLocations.Count.Should().Be(baselineRun.LogicalLocations.Count + currentRun.LogicalLocations.Count);

            // TODO: Add the rest of the tests in analogy to the artifacts test above.
        }
예제 #3
0
        public void RemapIndicesVisitor_RemapsNestedFilesProperly()
        {
            // This run has a single result that points to a doubly nested file
            Run baselineRun = CreateRunWithNestedFilesForTesting();

            // 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"
                        }
                    },
                },
                Results = new List <Result>
                {
                    new Result
                    {
                        Locations = new List <Location>
                        {
                            new Location
                            {
                                PhysicalLocation = new PhysicalLocation
                                {
                                    ArtifactLocation = new ArtifactLocation
                                    {
                                        Index = 0
                                    }
                                }
                            }
                        }
                    }
                }
            };

            Run mergedRun = currentRun.DeepClone();

            // How does this remapping work? First, we initialize the indices remapper
            // with a set of existing files (analagous to the files list of the most
            // recent log in a baselining situation). Next, visit a set of results
            // from the baseline itself, providing the historical set of file data
            // objects from the baseline. The visit does two things: 1) updates the
            // visitor files table with the superset of file data objects between
            // the baseline and current run, 2) updates individual results from the
            // baseline so that their file index values are correct.

            // 1. At this point the merged run consists of a copy of the current run.
            //    After visitor construction, we should see that the visitor's
            //    CurrentFiles property is equivalent to mergedRun.Files. The visitor
            //    has also been initialized with a dictionary that uses the complete
            //    file hierarchy as a key into the files array
            var visitor = new RemapIndicesVisitor(mergedRun.Artifacts);

            visitor.CurrentFiles.Should().BeEquivalentTo(mergedRun.Artifacts);
            visitor.RemappedFiles.Count.Should().Be(mergedRun.Artifacts.Count);


            // 2. We set HistoricalFiles to point to the old files array from the
            //    baseline run, then visit each baseline result. After each result
            //    visit, any file data objects (including parent files) for the
            //    result have been added to visitor.CurrentFiles, if missing. Each
            //    result fileIndex data is updated. We move the updated result
            //    objects to the merged run.

            // Various operations mutate state. We will make a copy so that we can
            // compare our ulitmate results to an unaltered original baseline
            var baselineRunCopy = baselineRun.DeepClone();

            visitor.HistoricalFiles = baselineRunCopy.Artifacts;
            foreach (Result result in baselineRunCopy.Results)
            {
                visitor.VisitResult(result);
                mergedRun.Results.Add(result);
            }

            // 3. After completing the results array visit, we'll grab the
            //    files array that represents all merged files from the runs.
            mergedRun.Artifacts = visitor.CurrentFiles;

            // We expect the merged files to be a superset of file data objects from the two runs
            mergedRun.Artifacts.Count.Should().Be(baselineRun.Artifacts.Count + currentRun.Artifacts.Count);

            // We expect that every merged file data has a corrected file index
            for (int i = 0; i < mergedRun.Artifacts.Count; i++)
            {
                mergedRun.Artifacts[i].Location.Index.Should().Be(i);
            }

            // We should see that the file index of the result we added should be pushed out by 1,
            // to account for the second run's file data objects being appended to the single
            // file data object in the first run.
            mergedRun.Artifacts[mergedRun.Results[0].Locations[0].PhysicalLocation.ArtifactLocation.Index].Contents.Text.Should()
            .Be(currentRun.Artifacts[currentRun.Results[0].Locations[0].PhysicalLocation.ArtifactLocation.Index].Contents.Text);

            // Similarly, we expect that all file data objects from the first run have been offset by one
            for (int i = 0; i < 3; i++)
            {
                baselineRun.Artifacts[i].Contents.Text.Should().Be(mergedRun.Artifacts[i + 1].Contents.Text);
            }

            // Finally, we should ensure that the parent index chain was updated properly on merging
            Artifact fileData = mergedRun.Artifacts[mergedRun.Results[1].Locations[0].PhysicalLocation.ArtifactLocation.Index];

            // Most nested
            fileData.ParentIndex.Should().Be(2);
            mergedRun.Artifacts[fileData.ParentIndex].ParentIndex.Should().Be(1);
            mergedRun.Artifacts[mergedRun.Artifacts[fileData.ParentIndex].ParentIndex].ParentIndex.Should().Be(-1);
        }