public void SuccessfulDotNetRunnerExecution_ReturnsDependencyGraph() { var mockFileSystem = new MockFileSystem(new Dictionary <string, MockFileData>()); // Arrange var dotNetRunner = new Mock <IDotNetRunner>(); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.IsAny <string[]>())) .Returns(new RunStatus(string.Empty, string.Empty, 0)) .Callback((string directory, string[] arguments) => { // Grab the temp filename that was passed... string tempFileName = arguments[3].Replace("/p:RestoreGraphOutputPath=", string.Empty).Trim('"'); // ... and stuff it with our dummy dependency graph mockFileSystem.AddFileFromEmbeddedResource(tempFileName, GetType().Assembly, "DotNetOutdated.Tests.TestData.test.dg"); }); var graphService = new DependencyGraphService(dotNetRunner.Object, mockFileSystem); // Act var dependencyGraph = graphService.GenerateDependencyGraph(Path); // Assert Assert.NotNull(dependencyGraph); }
public void EmptySolution_ReturnsEmptyDependencyGraph() { var mockFileSystem = new MockFileSystem(new Dictionary <string, MockFileData>()); // Arrange var dotNetRunner = new Mock <IDotNetRunner>(); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.Is <string[]>(a => a[0] == "msbuild" && a[2] == "/t:Restore,GenerateRestoreGraphFile"))) .Returns(new RunStatus(string.Empty, string.Empty, 0)) .Callback((string directory, string[] arguments) => { // Grab the temp filename that was passed... string tempFileName = arguments[3].Replace("/p:RestoreGraphOutputPath=", string.Empty).Trim('"'); // ... and stuff it with our dummy dependency graph mockFileSystem.AddFileFromEmbeddedResource(tempFileName, GetType().Assembly, "DotNetOutdated.Tests.TestData.empty.dg"); });; var graphService = new DependencyGraphService(dotNetRunner.Object, mockFileSystem); // Act var dependencyGraph = graphService.GenerateDependencyGraph(_solutionPath); // Assert Assert.NotNull(dependencyGraph); Assert.Equal(0, dependencyGraph.Projects.Count); dotNetRunner.Verify(runner => runner.Run(_path, It.Is <string[]>(a => a[0] == "msbuild" && a[1] == '\"' + _solutionPath + '\"' && a[2] == "/t:Restore,GenerateRestoreGraphFile"))); }
public void UnsuccessfulDotNetRunnerExecution_Throws() { var mockFileSystem = new MockFileSystem(new Dictionary <string, MockFileData>()); // Arrange var dotNetRunner = new Mock <IDotNetRunner>(); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.IsAny <string[]>())) .Returns(new RunStatus(string.Empty, string.Empty, 1)); var graphService = new DependencyGraphService(dotNetRunner.Object, mockFileSystem); // Assert Assert.Throws <CommandValidationException>(() => graphService.GenerateDependencyGraph(Path)); }
public void UnsuccessfulDotNetRunnerExecution_ReturnsNull() { var mockFileSystem = new MockFileSystem(new Dictionary <string, MockFileData>()); // Arrange var dotNetRunner = new Mock <IDotNetRunner>(); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.IsAny <string[]>())) .Returns(new RunStatus(string.Empty, string.Empty, 1)); var graphService = new DependencyGraphService(dotNetRunner.Object, mockFileSystem); // Act var dependencyGraph = graphService.GenerateDependencyGraph(Path); // Assert Assert.Null(dependencyGraph); }
public void SolutionPath_ReturnsDependencyGraphForAllProjects() { var mockFileSystem = new MockFileSystem(new Dictionary <string, MockFileData>()); // Arrange var dotNetRunner = new Mock <IDotNetRunner>(); string solutionProjects = string.Join(Environment.NewLine, "Project(s)", "-----------", _project1Path, _project2Path); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.Is <string[]>(a => a[0] == "sln"))) .Returns(new RunStatus(solutionProjects, string.Empty, 0)); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.Is <string[]>(a => a[0] == "msbuild"))) .Returns(new RunStatus(string.Empty, string.Empty, 0)) .Callback((string directory, string[] arguments) => { // Grab the temp filename that was passed... string tempFileName = arguments[3].Replace("/p:RestoreGraphOutputPath=", string.Empty).Trim('"'); // ... and stuff it with our dummy dependency graph string dependencyGraphFile = arguments[1] == '\"' + _project1Path + '\"' ? "test.dg" : "empty.dg"; mockFileSystem.AddFileFromEmbeddedResource(tempFileName, GetType().Assembly, "DotNetOutdated.Tests.TestData." + dependencyGraphFile); }); mockFileSystem.AddFileFromEmbeddedResource(_project1Path, GetType().Assembly, "DotNetOutdated.Tests.TestData.MicrosoftSdk.xml"); mockFileSystem.AddFileFromEmbeddedResource(_project2Path, GetType().Assembly, "DotNetOutdated.Tests.TestData.MicrosoftSdk.xml"); var graphService = new DependencyGraphService(dotNetRunner.Object, mockFileSystem); // Act var dependencyGraph = graphService.GenerateDependencyGraph(_solutionPath); // Assert Assert.NotNull(dependencyGraph); Assert.Equal(4, dependencyGraph.Projects.Count); dotNetRunner.Verify(runner => runner.Run(_path, It.Is <string[]>(a => a[0] == "sln" && a[2] == "list" && a[1] == '\"' + _solutionPath + '\"'))); dotNetRunner.Verify(runner => runner.Run(XFS.Path(@"c:\path\proj1"), It.Is <string[]>(a => a[0] == "msbuild" && a[1] == '\"' + _project1Path + '\"'))); dotNetRunner.Verify(runner => runner.Run(XFS.Path(@"c:\path\proj2"), It.Is <string[]>(a => a[0] == "msbuild" && a[1] == '\"' + _project2Path + '\"'))); }
public void EmptySolution_ReturnsEmptyDependencyGraph() { var mockFileSystem = new MockFileSystem(new Dictionary <string, MockFileData>()); // Arrange var dotNetRunner = new Mock <IDotNetRunner>(); string solutionProjects = string.Join(Environment.NewLine, "Project(s)", "-----------"); dotNetRunner.Setup(runner => runner.Run(It.IsAny <string>(), It.Is <string[]>(a => a[0] == "sln"))) .Returns(new RunStatus(solutionProjects, string.Empty, 0)); var graphService = new DependencyGraphService(dotNetRunner.Object, mockFileSystem); // Act var dependencyGraph = graphService.GenerateDependencyGraph(_solutionPath); // Assert Assert.NotNull(dependencyGraph); Assert.Equal(0, dependencyGraph.Projects.Count); dotNetRunner.Verify(runner => runner.Run(_path, It.Is <string[]>(a => a[0] == "sln" && a[2] == "list" && a[1] == '\"' + _solutionPath + '\"'))); dotNetRunner.Verify(runner => runner.Run(It.IsAny <string>(), It.Is <string[]>(a => a[0] == "msbuild")), Times.Never()); }
// TODO: Should we use framework here // // 1. Create dgspec.json via .NET Core CLI: GenerateRestoreGraphFile target // 2. Load dgspec.json into DependencyGraphSpec instance/object // For each SDK project (ProjectStyle.PackageReference) in the graph spec // 3. Get assets file (project.assets.json) for the project // 4. Construct LockFile from assets file for the project static void AnalyzeProject(string projectPath, string framework) { // Graph input var vertices = new HashSet <Node>(); var edges = new HashSet <Edge>(); var rootNuGetFramework = NuGetFramework.ParseFolder(framework); // TODO: optional filter // TODO: HACK...kan fjernes if (string.IsNullOrEmpty(projectPath)) { var rootPath = GetRepoRootPath(); //projectPath = Path.Combine(rootPath, "DotnetDependencies.sln"); projectPath = Path.Combine( Path.Combine( Path.Combine(rootPath, "src"), "Deps.CLI"), "Deps.CLI.csproj"); } // 'package graph' is a better word // Load dependency graph via nuget client tools build into msbuild (.NET Core CLI, .NET Core SDK) // TODO: opsatning af packageSources var dependencyGraphService = new DependencyGraphService(); var dependencyGraph = dependencyGraphService.GenerateDependencyGraph(projectPath); // We only support MSBuild style <PackageReference> SDK projects, where project.assets.json (lock file) is // generated in the RestoreOutputPath folder if (dependencyGraph.Projects.Any(p => p.RestoreMetadata.ProjectStyle != ProjectStyle.PackageReference)) { throw new InvalidOperationException("Only SDK projects are supported."); } PackageSpec projectRoot = dependencyGraph.Projects.Single(p => Path.GetFileName(p.FilePath) == Path.GetFileName(projectPath)); var rootNode = new ProjectReferenceNode(projectPath, projectRoot.Version.ToString(), rootNuGetFramework); vertices.Add(rootNode); var projectRootTargetFramework = projectRoot.TargetFrameworks.FirstOrDefault(tf => rootNuGetFramework.Equals(tf.FrameworkName)); if (projectRootTargetFramework == null) { throw new InvalidOperationException( $"The root project does not define the TargetFramework {rootNuGetFramework}"); } //Console.WriteLine($"project: {Path.GetFileName(project.FilePath)}"); //Console.WriteLine($"project.BaseDirectory: {Path.GetFileName(project.BaseDirectory)}"); //Console.WriteLine($"project.Name: {project.Name}"); //Console.WriteLine($"project.TargetFrameworks: {string.Join(", ", project.TargetFrameworks.Select(tfm => tfm.FrameworkName.GetShortFolderName()))}"); //Console.WriteLine($"project.Version: {project.Version}"); //Console.WriteLine($"project.RestoreMetadata.ProjectName: {project.RestoreMetadata.ProjectName}"); //Console.WriteLine($"project.RestoreMetadata.Sources: {string.Join(", ", project.RestoreMetadata.Sources)}"); //Console.WriteLine($"project.Dependencies: {string.Join(", ", project.Dependencies)}"); // TODO: Path. Unique name etc... Console.WriteLine($"Name: {projectRoot.Name}"); Console.WriteLine($"Version: {projectRoot.Version} ({rootNuGetFramework.GetShortFolderName()})"); Console.WriteLine($"Framework: {rootNuGetFramework.DotNetFrameworkName}"); Console.WriteLine($"Framework moniker: {rootNuGetFramework.GetShortFolderName()}"); Console.WriteLine(); Console.WriteLine("resolve project reference dependency graph"); Console.WriteLine(); // TODO: Path. Unique name etc... Console.WriteLine($"{projectRoot.Name}, v{projectRoot.Version} ({rootNuGetFramework.GetShortFolderName()})"); // // First resolve project reference dependency graph // foreach (ProjectRestoreMetadataFrameworkInfo targetFramework in projectRoot.RestoreMetadata.TargetFrameworks) { // only want project references defined for the TargetFramework if (!rootNuGetFramework.Equals(targetFramework.FrameworkName)) { continue; } // project references of a targetFramework foreach (var projectRestoreReference in targetFramework.ProjectReferences) { // TODO: PrivateAssets, ExcludeAssets, IncludeAssets //dependency.ProjectPath //dependency.ProjectUniqueName PackageSpec projectDependency = dependencyGraph.GetProjectSpec(projectRestoreReference.ProjectUniqueName); //Console.WriteLine($"({project.Name}, {Path.GetFileName(project.FilePath)}) is a project reference of ({projectRoot.Name}, {Path.GetFileName(projectRoot.FilePath)}"); NuGetFramework nearest = projectDependency.GetNearestFrameworkMatching(rootNuGetFramework); var projectReference = new ProjectReferenceNode(projectDependency.FilePath, projectDependency.Version.ToString(), nearest); vertices.Add(projectReference); edges.Add(new Edge(rootNode, projectReference)); ReportProjectReferences(rootNode, projectDependency, rootNuGetFramework, dependencyGraph, 1, vertices, edges); } } // TODO: What about ProjectReferences...are project references converted to package specs in dgspec file??? YES, // and available via dependencyGraph.Projects and dependencyGraph.GetProjectSpec // TODO: project Closure is part of graph all ready // closure[0] is the root // closure[1..m] contains project references IReadOnlyList <PackageSpec> projectClosure = dependencyGraph.GetClosure(projectRoot.RestoreMetadata.ProjectUniqueName); Debug.Assert(ReferenceEquals(projectRoot, projectClosure[0])); // // Second, resolve package reference dependency graph of each project // Console.WriteLine(); Console.WriteLine("resolve package reference dependency graph"); Console.WriteLine(); // for each csproj with PackageReference style (i.e. SDK csproj) foreach (PackageSpec project in dependencyGraph.Projects) { // TODO: Maybe just use the project.assets.json created by .NET Core SDK tooling // Generate lock file: A lock file has the package dependency graph for the project/solution/repo // that includes both the direct as well as transitive dependencies. var lockFileService = new LockFileService(); LockFile lockFile = lockFileService.GetLockFile(project.FilePath, project.RestoreMetadata.OutputPath); // TODO: This could change our code...we can get at the project references inside one single loop //{ // // TODO: For debugging // var projectDirectory = Path.GetDirectoryName(lockFile.PackageSpec.RestoreMetadata.ProjectPath); // // full path to the referenced msbuild files (csproj files) // var projectReferences = lockFile.Libraries // .Where(library => library.MSBuildProject != null) // .Select(library => Path.GetFullPath(Path.Combine(projectDirectory, library.MSBuildProject))) // .ToArray(); // if (projectReferences.Length > 0) // { // Console.WriteLine($"project references {string.Join(", ", projectReferences)}"); // } //} NuGetFramework nearest = project.GetNearestFrameworkMatching(rootNuGetFramework); TargetFrameworkInformation resolvedTargetFramework = project.TargetFrameworks.Single(tf => nearest.Equals(tf.FrameworkName)); // for the root should this resolve to the rootNuGetFramework Console.WriteLine($"{project.Name}, v{project.Version}, ({resolvedTargetFramework.FrameworkName.GetShortFolderName()})"); // Find the transitive closure for this tfm LockFileTarget lockFileTargetFramework = lockFile.Targets.FirstOrDefault(t => t.TargetFramework.Equals(resolvedTargetFramework.FrameworkName)); if (lockFileTargetFramework != null) { // For each direct dependency foreach (LibraryDependency directDependency in resolvedTargetFramework.Dependencies) { // Find the _resolved_ package reference LockFileTargetLibrary resolvedPackageReference = lockFileTargetFramework.Libraries.FirstOrDefault(library => library.Name == directDependency.Name); // Show transitive dependencies of this direct dependency ReportLockFilePackageDependencies(resolvedPackageReference, lockFileTargetFramework, 1); } } } }