public void GivenALogMessageWithNoTargetGraphsVerifyAllGraphsAreReturned() { var assetsFile = new LockFile(); assetsFile.Targets.Add(new LockFileTarget() { TargetFramework = NuGetFramework.Parse("net45"), }); assetsFile.Targets.Add(new LockFileTarget() { TargetFramework = NuGetFramework.Parse("net46"), RuntimeIdentifier = "win8" }); assetsFile.Targets.Add(new LockFileTarget() { TargetFramework = NuGetFramework.Parse("netstandard2.0") }); var expected1 = NuGetFramework.Parse("net45").DotNetFrameworkName; var expected2 = NuGetFramework.Parse("net46").DotNetFrameworkName + "/win8"; var expected3 = NuGetFramework.Parse("netstandard2.0").DotNetFrameworkName; // Create a message with no target graphs var message = new AssetsLogMessage(LogLevel.Warning, NuGetLogCode.NU1103, "test"); var graphs = message.GetTargetGraphs(assetsFile); graphs.Select(e => e.Name).ShouldBeEquivalentTo(new[] { expected1, expected2, expected3 }); }
public void WhenNoOpRestoreResult_LogMessagesAreSourcedFromTheCacheFile() { // Arrange var expectedLogLevel = NuGetLogCode.NU1500; var assetsLogMessage = new AssetsLogMessage(LogLevel.Error, NuGetLogCode.NU1501, "a"); var cacheLogMessage = new AssetsLogMessage(LogLevel.Error, expectedLogLevel, "a"); var lockFile = new LockFile(); lockFile.LogMessages.Add(assetsLogMessage); var cacheFile = new CacheFile("hash") { Success = true, LogMessages = new List <IAssetsLogMessage>() { cacheLogMessage }, }; // Act var result = new NoOpRestoreResult( success: true, lockFilePath: "project.assets.json", new Lazy <LockFile>(() => lockFile), cacheFile: cacheFile, cacheFilePath: "cachepath", projectStyle: ProjectStyle.PackageReference, elapsedTime: TimeSpan.MinValue); // Assert result.LogMessages.Should().NotBeNull(); result.LogMessages.Should().HaveCount(1); result.LogMessages.Single().Code.Should().Be(expectedLogLevel); }
public void GivenALogMessageWithMultipleGraphsVerifyTargetGraphsAreReturned() { var assetsFile = new LockFile(); assetsFile.Targets.Add(new LockFileTarget() { TargetFramework = NuGetFramework.Parse("net45"), }); assetsFile.Targets.Add(new LockFileTarget() { TargetFramework = NuGetFramework.Parse("net46"), RuntimeIdentifier = "win8" }); var expected1 = NuGetFramework.Parse("net45").DotNetFrameworkName; var expected2 = NuGetFramework.Parse("net46").DotNetFrameworkName + "/win8"; var message = new AssetsLogMessage(LogLevel.Warning, NuGetLogCode.NU1103, "test") { TargetGraphs = new[] { expected1, expected2 } }; var graphs = message.GetTargetGraphs(assetsFile); graphs.Select(e => e.Name).Should().BeEquivalentTo(new[] { expected1, expected2 }); }
public void GetMessagesForProject_WithMultipleMessages_SelectedMessagesForCorrectProject() { // Arrange var project1 = @"z:\src\solution\project1\project1.csproj"; var project2 = @"z:\src\solution\project2\project2.csproj"; var project1Error = RestoreLogMessage.CreateError(NuGetLogCode.NU1001, "project1 error"); project1Error.ProjectPath = project1; var project2Error = RestoreLogMessage.CreateError(NuGetLogCode.NU1002, "project2 error"); project2Error.ProjectPath = project2; var solutionMessages = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(project1Error), AssetsLogMessage.Create(project2Error) }; // Act IReadOnlyList <IAssetsLogMessage> actual = DependencyGraphSpecRequestProvider.GetMessagesForProject(solutionMessages, project1); // Assert var actualError = Assert.Single(actual); Assert.Equal(NuGetLogCode.NU1001, actualError.Code); }
public void WhenRestoreResult_LogMessagesAreSourcedFromTheAssetsFile() { // Arrange var expectedLogLevel = NuGetLogCode.NU1500; var assetsLogMessage = new AssetsLogMessage(LogLevel.Error, expectedLogLevel, "a"); var cacheLogMessage = new AssetsLogMessage(LogLevel.Error, NuGetLogCode.NU1501, "a"); var lockFile = new LockFile(); lockFile.LogMessages.Add(assetsLogMessage); var cacheFile = new CacheFile("hash") { Success = true, LogMessages = new List <IAssetsLogMessage>() { cacheLogMessage }, }; // Act var result = new RestoreResult( success: true, restoreGraphs: null, compatibilityCheckResults: null, lockFile: lockFile, previousLockFile: null, lockFilePath: "project.assets.json", msbuildFiles: Enumerable.Empty <MSBuildOutputFile>(), cacheFile: cacheFile, cacheFilePath: null, packagesLockFilePath: null, packagesLockFile: null, dependencyGraphSpecFilePath: null, dependencyGraphSpec: null, projectStyle: ProjectStyle.PackageReference, elapsedTime: TimeSpan.MinValue); // Assert result.LogMessages.Should().NotBeNull(); result.LogMessages.Should().HaveCount(1); result.LogMessages.Single().Code.Should().Be(expectedLogLevel); }
public void GivenALogMessageVerifyTargetGraphIsReturned() { var assetsFile = new LockFile(); assetsFile.Targets.Add(new LockFileTarget() { TargetFramework = NuGetFramework.Parse("net45"), }); assetsFile.Targets.Single().Libraries.Add(new LockFileTargetLibrary() { Name = "x", Version = NuGetVersion.Parse("1.0.0") }); var expected = NuGetFramework.Parse("net45").DotNetFrameworkName; var message = new AssetsLogMessage(LogLevel.Warning, NuGetLogCode.NU1103, "test", expected); var graphs = message.GetTargetGraphs(assetsFile); graphs.Select(e => e.Name).ShouldBeEquivalentTo(new[] { expected }); }
public async Task <RestoreResult> ExecuteAsync(CancellationToken token) { var restoreTime = Stopwatch.StartNew(); // Local package folders (non-sources) var localRepositories = new List <NuGetv3LocalRepository> { _request.DependencyProviders.GlobalPackages }; localRepositories.AddRange(_request.DependencyProviders.FallbackPackageFolders); var contextForProject = CreateRemoteWalkContext(_request, _logger); CacheFile cacheFile = null; if (NoOpRestoreUtilities.IsNoOpSupported(_request)) { var cacheFileAndStatus = EvaluateCacheFile(); cacheFile = cacheFileAndStatus.Key; if (cacheFileAndStatus.Value) { if (NoOpRestoreUtilities.VerifyAssetsAndMSBuildFilesAndPackagesArePresent(_request)) { // Replay Warnings and Errors from an existing lock file in case of a no-op. ReplayWarningsAndErrors(); restoreTime.Stop(); return(new NoOpRestoreResult( _success, _request.ExistingLockFile, _request.ExistingLockFile, _request.ExistingLockFile.Path, cacheFile, _request.Project.RestoreMetadata.CacheFilePath, _request.ProjectStyle, restoreTime.Elapsed)); } } } // Validate, for noop this will be replayed from the assets file. _success &= await ValidateProjectAsync(_request.Project, _logger); // Restore var graphs = await ExecuteRestoreAsync( _request.DependencyProviders.GlobalPackages, _request.DependencyProviders.FallbackPackageFolders, contextForProject, token); // Create assets file var assetsFile = BuildAssetsFile( _request.ExistingLockFile, _request.Project, graphs, localRepositories, contextForProject); _success &= await ValidateRestoreGraphsAsync(graphs, _logger); // Check package compatibility var checkResults = await VerifyCompatibilityAsync( _request.Project, _includeFlagGraphs, localRepositories, assetsFile, graphs, _request.ValidateRuntimeAssets, _logger); if (checkResults.Any(r => !r.Success)) { _success = false; } // Determine the lock file output path var assetsFilePath = GetAssetsFilePath(assetsFile); // Determine the cache file output path var cacheFilePath = NoOpRestoreUtilities.GetCacheFilePath(_request, assetsFile); // Tool restores are unique since the output path is not known until after restore if (_request.LockFilePath == null && _request.ProjectStyle == ProjectStyle.DotnetCliTool) { _request.LockFilePath = assetsFilePath; } // Generate Targets/Props files var msbuildOutputFiles = Enumerable.Empty <MSBuildOutputFile>(); if (contextForProject.IsMsBuildBased) { msbuildOutputFiles = BuildAssetsUtils.GetMSBuildOutputFiles( _request.Project, assetsFile, graphs, localRepositories, _request, assetsFilePath, _success, _logger); } // If the request is for a lower lock file version, downgrade it appropriately DowngradeLockFileIfNeeded(assetsFile); // Revert to the original case if needed await FixCaseForLegacyReaders(graphs, assetsFile, token); // Write the logs into the assets file var logs = _logger.Errors .Select(l => AssetsLogMessage.Create(l)) .ToList(); _success &= !logs.Any(l => l.Level == LogLevel.Error); assetsFile.LogMessages = logs; if (cacheFile != null) { cacheFile.Success = _success; } restoreTime.Stop(); // Create result return(new RestoreResult( _success, graphs, checkResults, msbuildOutputFiles, assetsFile, _request.ExistingLockFile, assetsFilePath, cacheFile, cacheFilePath, _request.ProjectStyle, restoreTime.Elapsed)); }
public async Task RestoreAsync_WithMinimalProjectAndAdditionalErrorMessage_WritesErrorsToAssetsFile() { // Arrange var projectName = "testproj"; var logger = new TestLogger(); using (var rootFolder = TestDirectory.Create()) { var projectFolder = new DirectoryInfo(Path.Combine(rootFolder, projectName)); projectFolder.Create(); var objFolder = projectFolder.CreateSubdirectory("obj"); var msbuildProjectPath = new FileInfo(Path.Combine(projectFolder.FullName, $"{projectName}.csproj")); var globalPackagesFolder = Path.Combine(rootFolder, "gpf"); var sources = new SourceRepository[0]; var restoreContext = new DependencyGraphCacheContext(logger, NullSettings.Instance); var solutionManager = new Mock <ISolutionManager>(); var restoreCommandProvidersCache = new RestoreCommandProvidersCache(); // When a VS nomination results in an exception, we use this minimal DGSpec to do a restore. var dgSpec = DependencyGraphSpecTestUtilities.CreateMinimalDependencyGraphSpec(msbuildProjectPath.FullName, objFolder.FullName); dgSpec.AddRestore(dgSpec.Projects[0].FilePath); // CpsPackageReferenceProject sets some additional properties, from settings, in GetPackageSpecsAndAdditionalMessages(...) dgSpec.Projects[0].RestoreMetadata.PackagesPath = globalPackagesFolder; // Having an "additional" error message is also critical var restoreLogMessage = new RestoreLogMessage(LogLevel.Error, NuGetLogCode.NU1000, "Test error") { FilePath = msbuildProjectPath.FullName, ProjectPath = msbuildProjectPath.FullName }; var additionalMessages = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(restoreLogMessage) }; // Act await DependencyGraphRestoreUtility.RestoreAsync( dgSpec, restoreContext, restoreCommandProvidersCache, cacheContextModifier : _ => { }, sources, parentId : Guid.Empty, forceRestore : false, isRestoreOriginalAction : true, additionalMessages, progressReporter : null, logger, CancellationToken.None); // Assert var assetsFilePath = Path.Combine(objFolder.FullName, "project.assets.json"); Assert.True(File.Exists(assetsFilePath), "Assets file does not exist"); LockFile assetsFile = new LockFileFormat().Read(assetsFilePath); IAssetsLogMessage actualMessage = Assert.Single(assetsFile.LogMessages); Assert.Equal(restoreLogMessage.Level, actualMessage.Level); Assert.Equal(restoreLogMessage.Code, actualMessage.Code); Assert.Equal(restoreLogMessage.Message, actualMessage.Message); } }
/// <summary> /// This is where the nominate calls for the IVs1 and IVS3 APIs combine. The reason for this method is to avoid duplication and potential issues /// The issue with this method is that it has some weird custom logging to ensure backward compatibility. It's on the implementer to ensure these calls are correct. /// <param name="projectUniqueName">projectUniqueName</param> /// <param name="projectRestoreInfo">projectRestoreInfo. Can be null</param> /// <param name="projectRestoreInfo2">proectRestoreInfo2. Can be null</param> /// <param name="token"></param> /// <remarks>Exactly one of projectRestoreInfos has to null.</remarks> /// <returns>The task that scheduled restore</returns> private Task <bool> NominateProjectAsync(string projectUniqueName, IVsProjectRestoreInfo projectRestoreInfo, IVsProjectRestoreInfo2 projectRestoreInfo2, CancellationToken token) { if (string.IsNullOrEmpty(projectUniqueName)) { throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(projectUniqueName)); } if (projectRestoreInfo == null && projectRestoreInfo2 == null) { throw new ArgumentNullException(nameof(projectRestoreInfo)); } if (projectRestoreInfo != null && projectRestoreInfo2 != null) { throw new ArgumentException($"Internal error: Both {nameof(projectRestoreInfo)} and {nameof(projectRestoreInfo2)} cannot have values. Please file an issue at NuGet/Home if you see this exception."); } if (projectRestoreInfo != null) { if (projectRestoreInfo.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } else { if (projectRestoreInfo2.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } try { _logger.LogInformation( $"The nominate API is called for '{projectUniqueName}'."); var projectNames = ProjectNames.FromFullProjectPath(projectUniqueName); DependencyGraphSpec dgSpec; IReadOnlyList <IAssetsLogMessage> nominationErrors = null; try { dgSpec = ToDependencyGraphSpec(projectNames, projectRestoreInfo, projectRestoreInfo2); } catch (Exception e) { var restoreLogMessage = RestoreLogMessage.CreateError(NuGetLogCode.NU1105, string.Format(Resources.NU1105, projectNames.ShortName, e.Message)); restoreLogMessage.LibraryId = projectUniqueName; nominationErrors = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(restoreLogMessage) }; var projectDirectory = Path.GetDirectoryName(projectUniqueName); string projectIntermediatePath = projectRestoreInfo == null ? projectRestoreInfo2.BaseIntermediatePath : projectRestoreInfo.BaseIntermediatePath; var dgSpecOutputPath = GetProjectOutputPath(projectDirectory, projectIntermediatePath); dgSpec = CreateMinimalDependencyGraphSpec(projectUniqueName, dgSpecOutputPath); } _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec, nominationErrors); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); } catch (OperationCanceledException) { throw; } catch (Exception e) { _logger.LogError(e.ToString()); TelemetryUtility.EmitException(nameof(VsSolutionRestoreService), nameof(NominateProjectAsync), e); return(Task.FromResult(false)); } }
public async Task <RestoreResult> ExecuteAsync(CancellationToken token) { using (var telemetry = TelemetryActivity.CreateTelemetryActivityWithNewOperationIdAndEvent(parentId: ParentId, eventName: ProjectRestoreInformation)) { _operationId = telemetry.OperationId; var restoreTime = Stopwatch.StartNew(); // Local package folders (non-sources) var localRepositories = new List <NuGetv3LocalRepository> { _request.DependencyProviders.GlobalPackages }; localRepositories.AddRange(_request.DependencyProviders.FallbackPackageFolders); var contextForProject = CreateRemoteWalkContext(_request, _logger); CacheFile cacheFile = null; using (var noOpTelemetry = TelemetryActivity.CreateTelemetryActivityWithNewOperationIdAndEvent(parentId: _operationId, eventName: RestoreNoOpInformation)) { if (NoOpRestoreUtilities.IsNoOpSupported(_request)) { noOpTelemetry.StartIntervalMeasure(); var cacheFileAndStatus = EvaluateCacheFile(); noOpTelemetry.EndIntervalMeasure(CacheFileEvaluateDuration); cacheFile = cacheFileAndStatus.Key; if (cacheFileAndStatus.Value) { noOpTelemetry.StartIntervalMeasure(); var noOpSuccess = NoOpRestoreUtilities.VerifyAssetsAndMSBuildFilesAndPackagesArePresent(_request); noOpTelemetry.EndIntervalMeasure(MsbuildAssetsVerificationDuration); noOpTelemetry.TelemetryEvent[MsbuildAssetsVerificationResult] = noOpSuccess; if (noOpSuccess) { noOpTelemetry.StartIntervalMeasure(); // Replay Warnings and Errors from an existing lock file in case of a no-op. await MSBuildRestoreUtility.ReplayWarningsAndErrorsAsync(_request.ExistingLockFile, _logger); noOpTelemetry.EndIntervalMeasure(ReplayLogsDuration); restoreTime.Stop(); return(new NoOpRestoreResult( _success, _request.ExistingLockFile, _request.ExistingLockFile, _request.ExistingLockFile.Path, cacheFile, _request.Project.RestoreMetadata.CacheFilePath, _request.ProjectStyle, restoreTime.Elapsed)); } } } } IEnumerable <RestoreTargetGraph> graphs = null; using (var restoreGraphTelemetry = TelemetryActivity.CreateTelemetryActivityWithNewOperationIdAndEvent(parentId: _operationId, eventName: GenerateRestoreGraph)) { // Restore graphs = await ExecuteRestoreAsync( _request.DependencyProviders.GlobalPackages, _request.DependencyProviders.FallbackPackageFolders, contextForProject, token, restoreGraphTelemetry); } LockFile assetsFile = null; using (TelemetryActivity.CreateTelemetryActivityWithNewOperationIdAndEvent(parentId: _operationId, eventName: GenerateAssetsFile)) { // Create assets file assetsFile = BuildAssetsFile( _request.ExistingLockFile, _request.Project, graphs, localRepositories, contextForProject); } IList <CompatibilityCheckResult> checkResults = null; using (TelemetryActivity.CreateTelemetryActivityWithNewOperationIdAndEvent(parentId: _operationId, eventName: ValidateRestoreGraphs)) { _success &= await ValidateRestoreGraphsAsync(graphs, _logger); // Check package compatibility checkResults = await VerifyCompatibilityAsync( _request.Project, _includeFlagGraphs, localRepositories, assetsFile, graphs, _request.ValidateRuntimeAssets, _logger); if (checkResults.Any(r => !r.Success)) { _success = false; } } // Generate Targets/Props files var msbuildOutputFiles = Enumerable.Empty <MSBuildOutputFile>(); string assetsFilePath = null; string cacheFilePath = null; using (TelemetryActivity.CreateTelemetryActivityWithNewOperationIdAndEvent(parentId: _operationId, eventName: CreateRestoreResult)) { // Determine the lock file output path assetsFilePath = GetAssetsFilePath(assetsFile); // Determine the cache file output path cacheFilePath = NoOpRestoreUtilities.GetCacheFilePath(_request, assetsFile); // Tool restores are unique since the output path is not known until after restore if (_request.LockFilePath == null && _request.ProjectStyle == ProjectStyle.DotnetCliTool) { _request.LockFilePath = assetsFilePath; } if (contextForProject.IsMsBuildBased) { msbuildOutputFiles = BuildAssetsUtils.GetMSBuildOutputFiles( _request.Project, assetsFile, graphs, localRepositories, _request, assetsFilePath, _success, _logger); } // If the request is for a lower lock file version, downgrade it appropriately DowngradeLockFileIfNeeded(assetsFile); // Revert to the original case if needed await FixCaseForLegacyReaders(graphs, assetsFile, token); // Write the logs into the assets file var logs = _logger.Errors .Select(l => AssetsLogMessage.Create(l)) .ToList(); _success &= !logs.Any(l => l.Level == LogLevel.Error); assetsFile.LogMessages = logs; if (cacheFile != null) { cacheFile.Success = _success; } var errorCodes = ConcatAsString(new HashSet <NuGetLogCode>(logs.Where(l => l.Level == LogLevel.Error).Select(l => l.Code))); var warningCodes = ConcatAsString(new HashSet <NuGetLogCode>(logs.Where(l => l.Level == LogLevel.Warning).Select(l => l.Code))); if (!string.IsNullOrEmpty(errorCodes)) { telemetry.TelemetryEvent[ErrorCodes] = errorCodes; } if (!string.IsNullOrEmpty(warningCodes)) { telemetry.TelemetryEvent[WarningCodes] = warningCodes; } telemetry.TelemetryEvent[RestoreSuccess] = _success; } restoreTime.Stop(); // Create result return(new RestoreResult( _success, graphs, checkResults, msbuildOutputFiles, assetsFile, _request.ExistingLockFile, assetsFilePath, cacheFile, cacheFilePath, _request.ProjectStyle, restoreTime.Elapsed)); } }