/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/CoreTest/SolutionTests/ProjectDependencyGraphTests.cs
741 строка
31 KB
Cyrus Najmabadi
Use file scoped namespaces. (#77854)
27 мар 2025, 09:55
Не верифицирован
27 мар 2025, 09:55
98d41b8
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.UnitTests; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Host.UnitTests; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.Workspace)] public sealed class ProjectDependencyGraphTests : TestBase { #region GetTopologicallySortedProjects [Fact] public void TestGetTopologicallySortedProjects() { VerifyTopologicalSort(CreateSolutionFromReferenceMap("A"), "A"); VerifyTopologicalSort(CreateSolutionFromReferenceMap("A B"), "AB", "BA"); VerifyTopologicalSort(CreateSolutionFromReferenceMap("C:A,B B:A A"), "ABC"); VerifyTopologicalSort(CreateSolutionFromReferenceMap("B:A A C:A D:C,B"), "ABCD", "ACBD"); } [Fact] public void TestTopologicallySortedProjectsIncrementalUpdate() { var solution = CreateSolutionFromReferenceMap("A"); VerifyTopologicalSort(solution, "A"); solution = AddProject(solution, "B"); VerifyTopologicalSort(solution, "AB", "BA"); } /// <summary> /// Verifies that <see cref="ProjectDependencyGraph.GetTopologicallySortedProjects(CancellationToken)"/> /// returns one of the correct results. /// </summary> /// <param name="solution"></param> /// <param name="expectedResults">A list of possible results. Because topological sorting is ambiguous /// in that a graph could have multiple topological sorts, this helper lets you give all the possible /// results and it asserts that one of them does match.</param> private static void VerifyTopologicalSort(Solution solution, params string[] expectedResults) { var projectDependencyGraph = solution.GetProjectDependencyGraph(); var projectIds = projectDependencyGraph.GetTopologicallySortedProjects(CancellationToken.None); var actualResult = string.Concat(projectIds.Select(id => solution.GetRequiredProject(id).AssemblyName)); Assert.Contains<string>(actualResult, expectedResults); } #endregion #region Dependency Sets [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542438")] public void TestGetDependencySets() { VerifyDependencySets(CreateSolutionFromReferenceMap("A B:A C:A D E:D F:D"), "ABC DEF"); VerifyDependencySets(CreateSolutionFromReferenceMap("A B:A,C C"), "ABC"); VerifyDependencySets(CreateSolutionFromReferenceMap("A B"), "A B"); VerifyDependencySets(CreateSolutionFromReferenceMap("A B C:B"), "A BC"); VerifyDependencySets(CreateSolutionFromReferenceMap("A B:A C:A D:B,C"), "ABCD"); } [Fact] public void TestDependencySetsIncrementalUpdate() { var solution = CreateSolutionFromReferenceMap("A"); VerifyDependencySets(solution, "A"); solution = AddProject(solution, "B"); VerifyDependencySets(solution, "A B"); } private static void VerifyDependencySets(Solution solution, string expectedResult) { var projectDependencyGraph = solution.GetProjectDependencyGraph(); var projectIds = projectDependencyGraph.GetDependencySets(CancellationToken.None); var actualResult = string.Join(" ", projectIds.Select( group => string.Concat( group.Select(p => solution.GetRequiredProject(p).AssemblyName).OrderBy(n => n))).OrderBy(n => n)); Assert.Equal(expectedResult, actualResult); } #endregion #region GetProjectsThatThisProjectTransitivelyDependsOn [Fact] public void TestGetProjectsThatThisProjectTransitivelyDependsOn() { VerifyTransitiveReferences(CreateSolutionFromReferenceMap("A"), "A", []); VerifyTransitiveReferences(CreateSolutionFromReferenceMap("B:A A"), "B", ["A"]); VerifyTransitiveReferences(CreateSolutionFromReferenceMap("C:B B:A A"), "C", ["B", "A"]); VerifyTransitiveReferences(CreateSolutionFromReferenceMap("C:B B:A A"), "A", []); } [Fact] public void TestGetProjectsThatThisProjectTransitivelyDependsOnThrowsArgumentNull() { var solution = CreateSolutionFromReferenceMap(""); Assert.Throws<ArgumentNullException>("projectId", () => solution.GetProjectDependencyGraph().GetProjectsThatThisProjectDirectlyDependsOn(null!)); } [Fact] public void TestTransitiveReferencesIncrementalUpdateInMiddle() { // We are going to create a solution with the references: // // A -> B -> C -> D // // but we will add the B -> C link last, to verify that when we add the B to C link we update the references of A. var solution = CreateSolutionFromReferenceMap("A B C D"); VerifyTransitiveReferences(solution, "A", []); VerifyTransitiveReferences(solution, "B", []); VerifyTransitiveReferences(solution, "C", []); VerifyTransitiveReferences(solution, "D", []); solution = AddProjectReferences(solution, "A", new string[] { "B" }); solution = AddProjectReferences(solution, "C", new string[] { "D" }); VerifyDirectReferences(solution, "A", ["B"]); VerifyDirectReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "A", ["B"]); VerifyTransitiveReferences(solution, "B", []); VerifyTransitiveReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "D", []); solution = AddProjectReferences(solution, "B", new string[] { "C" }); VerifyDirectReferences(solution, "B", ["C"]); VerifyTransitiveReferences(solution, "A", ["B", "C", "D"]); VerifyTransitiveReferences(solution, "B", ["C", "D"]); VerifyTransitiveReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "D", []); } [Fact] public void TestTransitiveReferencesIncrementalUpdateInMiddleLongerChain() { // We are going to create a solution with the references: // // A -> B -> C D -> E -> F // // but we will add the C-> D link last, to verify that when we add the C to D link we update the references of A. This is similar // to the previous test but with a longer chain. var solution = CreateSolutionFromReferenceMap("A:B B:C C D:E E:F F"); VerifyTransitiveReferences(solution, "A", ["B", "C"]); VerifyTransitiveReferences(solution, "B", ["C"]); VerifyTransitiveReferences(solution, "D", ["E", "F"]); VerifyTransitiveReferences(solution, "E", ["F"]); solution = AddProjectReferences(solution, "C", new string[] { "D" }); VerifyTransitiveReferences(solution, "A", ["B", "C", "D", "E", "F"]); } [Fact] public void TestTransitiveReferencesIncrementalUpdateWithReferencesAlreadyTransitivelyIncluded() { // We are going to create a solution with the references: // // A -> B -> C // // and then we'll add a reference from A -> C, and transitive references should be different var solution = CreateSolutionFromReferenceMap("A:B B:C C"); void VerifyAllTransitiveReferences() { VerifyTransitiveReferences(solution, "A", ["B", "C"]); VerifyTransitiveReferences(solution, "B", ["C"]); VerifyTransitiveReferences(solution, "C", []); } VerifyAllTransitiveReferences(); VerifyDirectReferences(solution, "A", ["B"]); solution = AddProjectReferences(solution, "A", new string[] { "C" }); VerifyAllTransitiveReferences(); VerifyDirectReferences(solution, "A", ["B", "C"]); } [Fact] public void TestTransitiveReferencesIncrementalUpdateWithProjectThatHasUnknownReferences() { // We are going to create a solution with the references: // // A B C -> D // // and then we will add a link from A to B. We won't ask for transitive references first, // so we shouldn't have any information for A, B, or C and have to deal with that. var solution = CreateSolutionFromReferenceMap("A B C:D D"); solution = solution.WithProjectReferences(solution.GetProjectsByName("C").Single().Id, []); VerifyTransitiveReferences(solution, "A", []); // At this point, we know the references for "A" (it's empty), but B and C's are still unknown. // At this point, we're also going to directly use the underlying project graph APIs; // the higher level solution APIs often call and ask for transitive information as well, which makes // this particularly hard to test -- it turns out the data we think is uncomputed might be computed prior // to adding the reference. var dependencyGraph = solution.GetProjectDependencyGraph(); var projectAId = solution.GetProjectsByName("A").Single().Id; var projectBId = solution.GetProjectsByName("B").Single().Id; dependencyGraph = dependencyGraph.WithAdditionalProjectReferences(projectAId, [new ProjectReference(projectBId)]); VerifyTransitiveReferences(solution, dependencyGraph, project: "A", expectedResults: ["B"]); } [Fact] public void TestTransitiveReferencesWithDanglingProjectReference() { // We are going to create a solution with the references: // // A -> B // // but we're going to add A as a reference with B not existing yet. Then we'll add in B and ask. var solution = CreateSolution(); var projectAId = ProjectId.CreateNewId("A"); var projectBId = ProjectId.CreateNewId("B"); var projectAInfo = ProjectInfo.Create(projectAId, VersionStamp.Create(), "A", "A", LanguageNames.CSharp, projectReferences: [new ProjectReference(projectBId)]); solution = solution.AddProject(projectAInfo); VerifyDirectReferences(solution, "A", []); VerifyTransitiveReferences(solution, "A", []); solution = solution.AddProject(projectBId, "B", "B", LanguageNames.CSharp); VerifyTransitiveReferences(solution, "A", ["B"]); } [Fact] public void TestTransitiveReferencesWithMultipleReferences() { // We are going to create a solution with the references: // // A B -> C D -> E // // and then add A referencing B and D in one call, to make sure that works. var solution = CreateSolutionFromReferenceMap("A B:C C D:E E"); VerifyTransitiveReferences(solution, "A", []); solution = AddProjectReferences(solution, "A", new string[] { "B", "D" }); VerifyDirectReferences(solution, "A", ["B", "D"]); VerifyTransitiveReferences(solution, "A", ["B", "C", "D", "E"]); } private static void VerifyDirectReferences(Solution solution, string project, string[] expectedResults) { var projectDependencyGraph = solution.GetProjectDependencyGraph(); var projectId = solution.GetProjectsByName(project).Single().Id; var projectIds = projectDependencyGraph.GetProjectsThatThisProjectDirectlyDependsOn(projectId); var actualResults = projectIds.Select(id => solution.GetRequiredProject(id).Name); Assert.Equal<string>( expectedResults.OrderBy(n => n), actualResults.OrderBy(n => n)); } private static void VerifyTransitiveReferences(Solution solution, string project, string[] expectedResults) { var projectDependencyGraph = solution.GetProjectDependencyGraph(); VerifyTransitiveReferences(solution, projectDependencyGraph, project, expectedResults); } private static void VerifyTransitiveReferences(Solution solution, ProjectDependencyGraph projectDependencyGraph, string project, string[] expectedResults) { var projectId = solution.GetProjectsByName(project).Single().Id; var projectIds = projectDependencyGraph.GetProjectsThatThisProjectTransitivelyDependsOn(projectId); var actualResults = projectIds.Select(id => solution.GetRequiredProject(id).Name); Assert.Equal<string>( expectedResults.OrderBy(n => n), actualResults.OrderBy(n => n)); } #endregion [Fact] public void TestDirectAndReverseDirectReferencesAfterWithProjectReferences() { var solution = CreateSolutionFromReferenceMap("A:B B"); VerifyDirectReverseReferences(solution, "B", ["A"]); solution = solution.WithProjectReferences(solution.GetProjectsByName("A").Single().Id, []); VerifyDirectReferences(solution, "A", []); VerifyDirectReverseReferences(solution, "B", []); } #region GetProjectsThatTransitivelyDependOnThisProject [Fact] public void TestGetProjectsThatTransitivelyDependOnThisProject() { VerifyReverseTransitiveReferences(CreateSolutionFromReferenceMap("A"), "A", []); VerifyReverseTransitiveReferences(CreateSolutionFromReferenceMap("B:A A"), "A", ["B"]); VerifyReverseTransitiveReferences(CreateSolutionFromReferenceMap("C:B B:A A"), "A", ["B", "C"]); VerifyReverseTransitiveReferences(CreateSolutionFromReferenceMap("C:B B:A A"), "C", []); VerifyReverseTransitiveReferences(CreateSolutionFromReferenceMap("D:C,B B:A C A"), "A", ["D", "B"]); } [Fact] public void TestGetProjectsThatTransitivelyDependOnThisProjectThrowsArgumentNull() { var solution = CreateSolutionFromReferenceMap(""); Assert.Throws<ArgumentNullException>("projectId", () => solution.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(null!)); } [Fact] public void TestReverseTransitiveReferencesIncrementalUpdateInMiddle() { // We are going to create a solution with the references: // // A -> B -> C -> D // // but we will add the B -> C link last, to verify that when we add the B to C link we update the reverse references of D. var solution = CreateSolutionFromReferenceMap("A B C D"); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", []); VerifyReverseTransitiveReferences(solution, "C", []); VerifyReverseTransitiveReferences(solution, "D", []); solution = AddProjectReferences(solution, "A", new string[] { "B" }); solution = AddProjectReferences(solution, "C", new string[] { "D" }); VerifyDirectReverseReferences(solution, "B", ["A"]); VerifyDirectReverseReferences(solution, "D", ["C"]); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); VerifyReverseTransitiveReferences(solution, "C", []); VerifyReverseTransitiveReferences(solution, "D", ["C"]); solution = AddProjectReferences(solution, "B", new string[] { "C" }); VerifyDirectReverseReferences(solution, "C", ["B"]); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); VerifyReverseTransitiveReferences(solution, "C", ["A", "B"]); VerifyReverseTransitiveReferences(solution, "D", ["A", "B", "C"]); } [Fact] public void TestReverseTransitiveReferencesForUnrelatedProjectAfterWithProjectReferences() { // We are going to create a solution with the references: // // A -> B C -> D // // and will then remove the reference from C to D. This process will cause us to throw out // all our caches, and asking for the reverse references of A will compute it again. var solution = CreateSolutionFromReferenceMap("A:B B C:D D"); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); VerifyReverseTransitiveReferences(solution, "C", []); VerifyReverseTransitiveReferences(solution, "D", ["C"]); solution = solution.WithProjectReferences(solution.GetProjectsByName("C").Single().Id, []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); } [Fact] public void TestForwardReferencesAfterProjectRemoval() { // We are going to create a solution with the references: // // A -> B -> C -> D // // and will then remove project B. var solution = CreateSolutionFromReferenceMap("A:B B:C C:D D"); VerifyDirectReferences(solution, "A", ["B"]); VerifyDirectReferences(solution, "B", ["C"]); VerifyDirectReferences(solution, "C", ["D"]); VerifyDirectReferences(solution, "D", []); solution = solution.RemoveProject(solution.GetProjectsByName("B").Single().Id); VerifyDirectReferences(solution, "A", []); VerifyDirectReferences(solution, "C", ["D"]); VerifyDirectReferences(solution, "D", []); } [Fact] public void TestForwardTransitiveReferencesAfterProjectRemoval() { // We are going to create a solution with the references: // // A -> B -> C -> D // // and will then remove project B. var solution = CreateSolutionFromReferenceMap("A:B B:C C:D D"); VerifyTransitiveReferences(solution, "A", ["B", "C", "D"]); VerifyTransitiveReferences(solution, "B", ["C", "D"]); VerifyTransitiveReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "D", []); solution = solution.RemoveProject(solution.GetProjectsByName("B").Single().Id); VerifyTransitiveReferences(solution, "A", []); VerifyTransitiveReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "D", []); } [Fact] public void TestReverseReferencesAfterProjectRemoval() { // We are going to create a solution with the references: // // A -> B -> C -> D // // and will then remove project B. var solution = CreateSolutionFromReferenceMap("A:B B:C C:D D"); VerifyDirectReverseReferences(solution, "A", []); VerifyDirectReverseReferences(solution, "B", ["A"]); VerifyDirectReverseReferences(solution, "C", ["B"]); VerifyDirectReverseReferences(solution, "D", ["C"]); solution = solution.RemoveProject(solution.GetProjectsByName("B").Single().Id); VerifyDirectReverseReferences(solution, "A", []); VerifyDirectReverseReferences(solution, "C", []); VerifyDirectReverseReferences(solution, "D", ["C"]); } [Fact] public void TestReverseTransitiveReferencesAfterProjectRemoval() { // We are going to create a solution with the references: // // A -> B -> C -> D // // and will then remove project B. var solution = CreateSolutionFromReferenceMap("A:B B:C C:D D"); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); VerifyReverseTransitiveReferences(solution, "C", ["A", "B"]); VerifyReverseTransitiveReferences(solution, "D", ["A", "B", "C"]); solution = solution.RemoveProject(solution.GetProjectsByName("B").Single().Id); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "C", []); VerifyReverseTransitiveReferences(solution, "D", ["C"]); } [Fact] public void TestReverseTransitiveReferencesAfterProjectReferenceRemoval_PreserveThroughUnrelatedSequence() { // We are going to create a solution with the references: // // A -> B -> C // \ // > D // // and will then remove the project reference A->B. This test verifies that the new project dependency graph // did not lose previously-computed information about the transitive reverse references for D. var solution = CreateSolutionFromReferenceMap("A:B,D B:C C D"); VerifyReverseTransitiveReferences(solution, "D", ["A"]); var a = solution.GetProjectsByName("A").Single(); var b = solution.GetProjectsByName("B").Single(); var d = solution.GetProjectsByName("D").Single(); var expected = solution.SolutionState.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(d.Id); var aToB = a.ProjectReferences.Single(reference => reference.ProjectId == b.Id); solution = solution.RemoveProjectReference(a.Id, aToB); // Before any other operations, verify that TryGetProjectsThatTransitivelyDependOnThisProject returns a // non-null set. Specifically, it returns the _same_ set that was computed prior to the project reference // removal. Assert.Same(expected, solution.SolutionState.GetProjectDependencyGraph().GetTestAccessor().TryGetProjectsThatTransitivelyDependOnThisProject(d.Id)); } [Fact] public void TestReverseTransitiveReferencesAfterProjectReferenceRemoval_PreserveUnrelated() { // We are going to create a solution with the references: // // A -> B -> C // D -> E // // and will then remove the project reference A->B. This test verifies that the new project dependency graph // did not lose previously-computed information about the transitive reverse references for E. var solution = CreateSolutionFromReferenceMap("A:B B:C C D:E E"); VerifyReverseTransitiveReferences(solution, "E", ["D"]); var a = solution.GetProjectsByName("A").Single(); var b = solution.GetProjectsByName("B").Single(); var e = solution.GetProjectsByName("E").Single(); var expected = solution.SolutionState.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(e.Id); var aToB = a.ProjectReferences.Single(reference => reference.ProjectId == b.Id); solution = solution.RemoveProjectReference(a.Id, aToB); // Before any other operations, verify that TryGetProjectsThatTransitivelyDependOnThisProject returns a // non-null set. Specifically, it returns the _same_ set that was computed prior to the project reference // removal. Assert.Same(expected, solution.SolutionState.GetProjectDependencyGraph().GetTestAccessor().TryGetProjectsThatTransitivelyDependOnThisProject(e.Id)); } [Fact] public void TestReverseTransitiveReferencesAfterProjectReferenceRemoval_DiscardImpacted() { // We are going to create a solution with the references: // // A -> B -> C // \ // > D // // and will then remove the project reference A->B. This test verifies that the new project dependency graph // discards previously-computed information about the transitive reverse references for C. var solution = CreateSolutionFromReferenceMap("A:B,D B:C C D"); VerifyReverseTransitiveReferences(solution, "C", ["A", "B"]); var a = solution.GetProjectsByName("A").Single(); var b = solution.GetProjectsByName("B").Single(); var c = solution.GetProjectsByName("C").Single(); var notExpected = solution.SolutionState.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(c.Id); Assert.NotNull(notExpected); var aToB = a.ProjectReferences.Single(reference => reference.ProjectId == b.Id); solution = solution.RemoveProjectReference(a.Id, aToB); // Before any other operations, verify that TryGetProjectsThatTransitivelyDependOnThisProject returns a // null set. Assert.Null(solution.SolutionState.GetProjectDependencyGraph().GetTestAccessor().TryGetProjectsThatTransitivelyDependOnThisProject(c.Id)); VerifyReverseTransitiveReferences(solution, "C", ["B"]); } [Fact] public void TestSameDependencyGraphAfterOneOfMultipleReferencesRemoved() { // We are going to create a solution with the references: // // A -> B -> C -> D // \__^ // // This solution has multiple references from B->C. We will remove one reference from B->C and verify that // the project dependency graph in the solution state did not change (reference equality). // // We then remove the second reference, and verify that the dependency graph does change. var solution = CreateSolutionFromReferenceMap("A:B B:C,C C:D D"); VerifyDirectReferences(solution, "A", ["B"]); VerifyDirectReferences(solution, "B", ["C"]); VerifyDirectReferences(solution, "C", ["D"]); VerifyDirectReferences(solution, "D", []); VerifyTransitiveReferences(solution, "A", ["B", "C", "D"]); VerifyTransitiveReferences(solution, "B", ["C", "D"]); VerifyTransitiveReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "D", []); VerifyDirectReverseReferences(solution, "A", []); VerifyDirectReverseReferences(solution, "B", ["A"]); VerifyDirectReverseReferences(solution, "C", ["B"]); VerifyDirectReverseReferences(solution, "D", ["C"]); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); VerifyReverseTransitiveReferences(solution, "C", ["A", "B"]); VerifyReverseTransitiveReferences(solution, "D", ["A", "B", "C"]); var dependencyGraph = solution.SolutionState.GetProjectDependencyGraph(); Assert.NotNull(dependencyGraph); var b = solution.GetProjectsByName("B").Single(); var c = solution.GetProjectsByName("C").Single(); var firstBToC = b.ProjectReferences.First(reference => reference.ProjectId == c.Id); solution = solution.RemoveProjectReference(b.Id, firstBToC); Assert.Same(dependencyGraph, solution.SolutionState.GetProjectDependencyGraph()); b = solution.GetProjectsByName("B").Single(); var remainingBToC = b.ProjectReferences.Single(reference => reference.ProjectId == c.Id); solution = solution.RemoveProjectReference(b.Id, remainingBToC); Assert.NotSame(dependencyGraph, solution.SolutionState.GetProjectDependencyGraph()); VerifyDirectReferences(solution, "A", ["B"]); VerifyDirectReferences(solution, "B", []); VerifyDirectReferences(solution, "C", ["D"]); VerifyDirectReferences(solution, "D", []); VerifyTransitiveReferences(solution, "A", ["B"]); VerifyTransitiveReferences(solution, "B", []); VerifyTransitiveReferences(solution, "C", ["D"]); VerifyTransitiveReferences(solution, "D", []); VerifyDirectReverseReferences(solution, "A", []); VerifyDirectReverseReferences(solution, "B", ["A"]); VerifyDirectReverseReferences(solution, "C", []); VerifyDirectReverseReferences(solution, "D", ["C"]); VerifyReverseTransitiveReferences(solution, "A", []); VerifyReverseTransitiveReferences(solution, "B", ["A"]); VerifyReverseTransitiveReferences(solution, "C", []); VerifyReverseTransitiveReferences(solution, "D", ["C"]); } private static void VerifyDirectReverseReferences(Solution solution, string project, string[] expectedResults) { var projectDependencyGraph = solution.GetProjectDependencyGraph(); var projectId = solution.GetProjectsByName(project).Single().Id; var projectIds = projectDependencyGraph.GetProjectsThatDirectlyDependOnThisProject(projectId); var actualResults = projectIds.Select(id => solution.GetRequiredProject(id).Name); Assert.Equal<string>( expectedResults.OrderBy(n => n), actualResults.OrderBy(n => n)); } private static void VerifyReverseTransitiveReferences(Solution solution, string project, string[] expectedResults) { var projectDependencyGraph = solution.GetProjectDependencyGraph(); var projectId = solution.GetProjectsByName(project).Single().Id; var projectIds = projectDependencyGraph.GetProjectsThatTransitivelyDependOnThisProject(projectId); var actualResults = projectIds.Select(id => solution.GetRequiredProject(id).Name); Assert.Equal<string>( expectedResults.OrderBy(n => n), actualResults.OrderBy(n => n)); } #endregion #region Helpers private static Solution CreateSolutionFromReferenceMap(string projectReferences) { var solution = CreateSolution(); var references = new Dictionary<string, IEnumerable<string>>(); var projectDefinitions = projectReferences.Split(' '); foreach (var projectDefinition in projectDefinitions) { var projectDefinitionParts = projectDefinition.Split(':'); string[]? referencedProjectNames = null; if (projectDefinitionParts.Length == 2) { referencedProjectNames = projectDefinitionParts[1].Split(','); } else if (projectDefinitionParts.Length != 1) { throw new ArgumentException("Invalid project definition: " + projectDefinition); } var projectName = projectDefinitionParts[0]; if (referencedProjectNames != null) { references.Add(projectName, referencedProjectNames); } solution = AddProject(solution, projectName); } foreach (var (name, refs) in references) solution = AddProjectReferences(solution, name, refs); return solution; } private static Solution AddProject(Solution solution, string projectName) { var projectId = ProjectId.CreateNewId(debugName: projectName); return solution.AddProject(ProjectInfo.Create(projectId, VersionStamp.Create(), projectName, projectName, LanguageNames.CSharp, projectName)); } private static Solution AddProjectReferences(Solution solution, string projectName, IEnumerable<string> projectReferences) { var referencesByTargetProject = new Dictionary<string, List<ProjectReference>>(); foreach (var targetProject in projectReferences) { var references = referencesByTargetProject.GetOrAdd(targetProject, _ => []); if (references.Count == 0) { references.Add(new ProjectReference(solution.GetProjectsByName(targetProject).Single().Id)); } else { references.Add(new ProjectReference(solution.GetProjectsByName(targetProject).Single().Id, [$"alias{references.Count}"])); } } return solution.AddProjectReferences( solution.GetProjectsByName(projectName).Single().Id, referencesByTargetProject.SelectMany(pair => pair.Value)); } private static Solution CreateSolution() => new AdhocWorkspace().CurrentSolution; #endregion }