diff --git a/build/Shared/EqualityUtility.cs b/build/Shared/EqualityUtility.cs index 11c57acaaa2..accd0552ad7 100644 --- a/build/Shared/EqualityUtility.cs +++ b/build/Shared/EqualityUtility.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -59,6 +59,37 @@ internal static bool SequenceEqualWithNullCheck( return self.SequenceEqual(other, comparer); } + /// + /// Compares two sets for equality, allowing either sequence to be null. + /// If one is null, both have to be null for equality. + /// + internal static bool SetEqualsWithNullCheck( + this ISet self, + ISet other, + IEqualityComparer comparer = null) + { + bool identityEquals; + if (TryIdentityEquals(self, other, out identityEquals)) + { + return identityEquals; + } + + // Verify they could be equal by count + if (self.Count != other.Count) + { + return false; + } + + if (comparer == null) + { + comparer = EqualityComparer.Default; + } + + var set = new HashSet(self, comparer); + + return set.SetEquals(other); + } + internal static bool DictionaryEquals( IDictionary self, IDictionary other, @@ -74,6 +105,12 @@ internal static bool DictionaryEquals( return identityEquals; } + // Verify they could be equal by count + if (self.Count != other.Count) + { + return false; + } + if (!self.Keys.OrderedEquals( other.Keys, s => s, diff --git a/build/Shared/SharedExtensions.cs b/build/Shared/SharedExtensions.cs index 54935620a96..08be4a3f6df 100644 --- a/build/Shared/SharedExtensions.cs +++ b/build/Shared/SharedExtensions.cs @@ -25,9 +25,36 @@ public static List AsList(this IEnumerable enumerable) return new List(enumerable); } + /// + /// Return the ISet as a HashSet of T, copying if required. Optimized for common case where it is a HashSet of T. + /// Avoid mutating the return value. + /// + public static HashSet AsHashSet(this ISet enumerable, IEqualityComparer comparer = null) + { + if (enumerable == null) + { + return null; + } + + var set = enumerable as HashSet; + if (set != null) + { + return set; + } + else + { + if (comparer == null) + { + comparer = EqualityComparer.Default; + } + + return new HashSet(enumerable, comparer); + } + } + public static void ForEach(this IEnumerable enumeration, Action action) { - foreach (T item in enumeration) + foreach (var item in enumeration) { action(item); } diff --git a/build/common.project.props b/build/common.project.props index dafd8e777d3..dadb2df9208 100644 --- a/build/common.project.props +++ b/build/common.project.props @@ -243,7 +243,8 @@ diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommandRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommandRunner.cs index d9643c10ba1..445de72cb0a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommandRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommandRunner.cs @@ -138,7 +138,7 @@ public async Task ExecuteCommand(PackageReferenceArgs packageReferenceArgs, } // Ignore the graphs with RID else if (compatibleFrameworks.Count == - restorePreviewResult.Result.CompatibilityCheckResults.Where(r => r.Graph.RuntimeIdentifier == null).Count()) + restorePreviewResult.Result.CompatibilityCheckResults.Where(r => string.IsNullOrEmpty(r.Graph.RuntimeIdentifier)).Count()) { // Package is compatible with all the project TFMs // Add an unconditional package reference to the project diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/PackageSpecificWarningProperties.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/PackageSpecificWarningProperties.cs index f5db18bf2ef..5aa30493632 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/PackageSpecificWarningProperties.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/PackageSpecificWarningProperties.cs @@ -1,11 +1,13 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; +using System.Linq; using NuGet.Common; using NuGet.Frameworks; using NuGet.ProjectModel; +using NuGet.Shared; namespace NuGet.Commands { @@ -13,14 +15,14 @@ namespace NuGet.Commands /// /// Contains Package specific properties for Warnings. /// - public class PackageSpecificWarningProperties + public class PackageSpecificWarningProperties : IEquatable { /// /// Contains Package specific No warn properties. /// NuGetLogCode -> LibraryId -> Set of Frameworks. /// - private IDictionary>> Properties; + public IDictionary>> Properties { get; private set; } /// /// Extracts PackageSpecific WarningProperties from a PackageSpec @@ -36,7 +38,7 @@ public static PackageSpecificWarningProperties CreatePackageSpecificWarningPrope { foreach (var framework in packageSpec.TargetFrameworks) { - warningProperties.AddRange(dependency.NoWarn, dependency.Name, framework.FrameworkName); + warningProperties.AddRangeOfCodes(dependency.NoWarn, dependency.Name, framework.FrameworkName); } } @@ -44,13 +46,40 @@ public static PackageSpecificWarningProperties CreatePackageSpecificWarningPrope { foreach (var dependency in framework.Dependencies) { - warningProperties.AddRange(dependency.NoWarn, dependency.Name, framework.FrameworkName); + warningProperties.AddRangeOfCodes(dependency.NoWarn, dependency.Name, framework.FrameworkName); } } return warningProperties; } + /// + /// Extracts PackageSpecific WarningProperties from a PackageSpec for a specific NuGetFramework + /// + /// PackageSpec containing the Dependencies with WarningProperties + /// NuGetFramework for which the properties should be assessed. + /// PackageSpecific WarningProperties extracted from a PackageSpec for a specific NuGetFramework + public static PackageSpecificWarningProperties CreatePackageSpecificWarningProperties(PackageSpec packageSpec, + NuGetFramework framework) + { + // NuGetLogCode -> LibraryId -> Set of Frameworks. + var warningProperties = new PackageSpecificWarningProperties(); + + foreach (var dependency in packageSpec.Dependencies) + { + warningProperties.AddRangeOfCodes(dependency.NoWarn, dependency.Name, framework); + } + + var targetFrameworkInformation = packageSpec.GetTargetFramework(framework); + + foreach (var dependency in targetFrameworkInformation.Dependencies) + { + warningProperties.AddRangeOfCodes(dependency.NoWarn, dependency.Name, framework); + } + + return warningProperties; + } + /// /// Adds a NuGetLogCode into the NoWarn Set for the specified library Id and target graph. /// @@ -85,7 +114,7 @@ public void Add(NuGetLogCode code, string libraryId, NuGetFramework framework) /// IEnumerable of NuGetLogCode for which no warning should be thrown. /// Library for which no warning should be thrown. /// Target graph for which no warning should be thrown. - public void AddRange(IEnumerable codes, string libraryId, NuGetFramework framework) + public void AddRangeOfCodes(IEnumerable codes, string libraryId, NuGetFramework framework) { foreach (var code in codes) { @@ -93,6 +122,20 @@ public void AddRange(IEnumerable codes, string libraryId, NuGetFra } } + /// + /// Adds a list of NuGetLogCode into the NoWarn Set for the specified library Id and target graph. + /// + /// NuGetLogCode for which no warning should be thrown. + /// Library for which no warning should be thrown. + /// IEnumerable of Target graph for which no warning should be thrown. + public void AddRangeOfFrameworks(NuGetLogCode code, string libraryId, IEnumerable frameworks) + { + foreach (var framework in frameworks) + { + Add(code, libraryId, framework); + } + } + /// /// Checks if a NugetLogCode is part of the NoWarn list for the specified library Id and target graph. /// @@ -107,5 +150,34 @@ public bool Contains(NuGetLogCode code, string libraryId, NuGetFramework framewo libraryIdsAndFrameworks.TryGetValue(libraryId, out var frameworkSet) && frameworkSet.Contains(framework); } + + public override int GetHashCode() + { + // return a constant hash for all objects since the contents of Properties are mutable + return 1; + } + + public override bool Equals(object obj) + { + return Equals(obj as PackageSpecificWarningProperties); + } + + public bool Equals(PackageSpecificWarningProperties other) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return EqualityUtility.DictionaryEquals( + Properties, + other.Properties, + (sv1, ov1) => EqualityUtility.DictionaryEquals(sv1, ov1, (sv2, ov2) => EqualityUtility.SetEqualsWithNullCheck(sv2, ov2))); + } } } diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/RestoreCollectorLogger.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/RestoreCollectorLogger.cs index 8d63a1b9194..d739f80658f 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/RestoreCollectorLogger.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/RestoreCollectorLogger.cs @@ -1,11 +1,14 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using NuGet.Common; +using NuGet.ProjectModel; +using NuGet.Shared; namespace NuGet.Commands { @@ -14,12 +17,66 @@ public class RestoreCollectorLogger : LoggerBase, ICollectorLogger private readonly ILogger _innerLogger; private readonly ConcurrentQueue _errors; private readonly bool _hideWarningsAndErrors; + private IEnumerable _restoreTargetGraphs; + private PackageSpec _projectSpec; + private WarningPropertiesCollection _transitiveWarningPropertiesCollection; + + public string ProjectPath => _projectSpec?.RestoreMetadata?.ProjectPath; public IEnumerable Errors => _errors.ToArray(); - public WarningPropertiesCollection WarningPropertiesCollection { get; set; } - - public string ProjectPath { get; set; } + public WarningPropertiesCollection ProjectWarningPropertiesCollection { get; set; } + + public WarningPropertiesCollection TransitiveWarningPropertiesCollection + { + get + { + if (_transitiveWarningPropertiesCollection == null) + { + // Populate TransitiveWarningPropertiesCollection only if it is null and we have RestoreTargetGraphs. + // This will happen at most once and only if we have the project spec with restore metadata. + if (_restoreTargetGraphs != null && + _restoreTargetGraphs.Any() && + _projectSpec != null && + _projectSpec.RestoreMetadata != null) + { + TransitiveWarningPropertiesCollection = TransitiveNoWarnUtils.CreateTransitiveWarningPropertiesCollection( + _restoreTargetGraphs, + _projectSpec); + } + } + + return _transitiveWarningPropertiesCollection; + } + + set => _transitiveWarningPropertiesCollection = value; + } + + /// + /// Stores a reference to PackageSpec for the project from the restore request. + /// This are used to generate the warning properties for the project. + /// + /// PackageSpec to be stored for reference. + public void ApplyRestoreInputs(PackageSpec projectSpec) + { + _projectSpec = projectSpec; + + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + projectSpec.RestoreMetadata?.ProjectWideWarningProperties, + PackageSpecificWarningProperties.CreatePackageSpecificWarningProperties(projectSpec), + projectSpec.TargetFrameworks.Select(f => f.FrameworkName).AsList().AsReadOnly() + ); + } + + /// + /// Stores a reference to RestoreTargetGraphs from the restore output. + /// These graphs are used to generate the transitive warning properties. + /// + /// RestoreTargetGraphs to be stored for reference. + public void ApplyRestoreOutput(IEnumerable restoreTargetGraphs) + { + _restoreTargetGraphs = restoreTargetGraphs; + } /// /// Initializes an instance of the , while still @@ -70,9 +127,12 @@ public RestoreCollectorLogger(ILogger innerLogger) public void Log(IRestoreLogMessage message) { - // This will be true only when the Message is a Warning and should be suppressed. - if (WarningPropertiesCollection == null || !WarningPropertiesCollection.ApplyWarningProperties(message)) + // check if the message is a warning and it is suppressed + if (!IsWarningSuppressed(message)) { + // if the message is not suppressed then check if it needs to be upgraded to an error + UpgradeWarningToErrorIfNeeded(message); + if (string.IsNullOrEmpty(message.FilePath)) { message.FilePath = message.ProjectPath ?? ProjectPath; @@ -92,10 +152,12 @@ public void Log(IRestoreLogMessage message) public Task LogAsync(IRestoreLogMessage message) { - - // This will be true only when the Message is a Warning and should be suppressed. - if (WarningPropertiesCollection == null || !WarningPropertiesCollection.ApplyWarningProperties(message)) + // check if the message is a warning and it is suppressed + if (!IsWarningSuppressed(message)) { + // if the message is not suppressed then check if it needs to be upgraded to an error + UpgradeWarningToErrorIfNeeded(message); + if (string.IsNullOrEmpty(message.FilePath)) { message.FilePath = message.ProjectPath ?? ProjectPath; @@ -139,7 +201,44 @@ protected bool DisplayMessage(IRestoreLogMessage message) else { return (message.Level >= VerbosityLevel); - } + } + } + + /// + /// This method checks if at least one of the warning properties collections is not null and it suppresses the warning. + /// + /// IRestoreLogMessage to be logged. + /// bool indicating if the message should be suppressed. + private bool IsWarningSuppressed(IRestoreLogMessage message) + { + if (message.Level == LogLevel.Warning) + { + // If the ProjectWarningPropertiesCollection is present then test if the warning is suppressed in + // project wide no warn or package specific no warn + if (ProjectWarningPropertiesCollection?.ApplyNoWarnProperties(message) == true) + { + return true; + } + else + { + // Use transitive warning properties only if the project does not suppress the warning + // In transitive warning properties look at only the package specific ones as all properties are per package reference. + return TransitiveWarningPropertiesCollection?.ApplyNoWarnProperties(message) == true; + } + } + + return false; + } + + /// + /// This method upgrades the warning to an error if the project wide warning properties have set the code in WarningsAsErrors or + /// set TreatWarningsAsErrors to true + /// + /// IRestoreLogMessage to be logged as an error or warning. + /// bool indicating if the message should be suppressed. + private void UpgradeWarningToErrorIfNeeded(IRestoreLogMessage message) + { + ProjectWarningPropertiesCollection?.ApplyWarningAsErrorProperties(message); } private static IRestoreLogMessage ToRestoreLogMessage(ILogMessage message) @@ -154,4 +253,4 @@ private static IRestoreLogMessage ToRestoreLogMessage(ILogMessage message) return restoreLogMessage; } } -} +} \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/TransitiveNoWarnUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/TransitiveNoWarnUtils.cs new file mode 100644 index 00000000000..b1772f7b835 --- /dev/null +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/TransitiveNoWarnUtils.cs @@ -0,0 +1,916 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NuGet.Common; +using NuGet.DependencyResolver; +using NuGet.Frameworks; +using NuGet.LibraryModel; +using NuGet.ProjectModel; +using NuGet.Shared; + + +namespace NuGet.Commands +{ + public static class TransitiveNoWarnUtils + { + + /// + /// Creates a PackageSpecificWarningProperties for a project generated by traversing the dependency graph. + /// + /// Parent project restore target graphs. + /// PackageSpec of the parent project. + /// WarningPropertiesCollection with the project frameworks and the transitive package specific no warn properties. + public static WarningPropertiesCollection CreateTransitiveWarningPropertiesCollection( + IEnumerable targetGraphs, + PackageSpec parentProjectSpec) + { + var transitivePackageSpecificProperties = new PackageSpecificWarningProperties(); + var projectFrameworks = new List(); + var parentWarningProperties = new WarningPropertiesCollection( + parentProjectSpec.RestoreMetadata?.ProjectWideWarningProperties, + PackageSpecificWarningProperties.CreatePackageSpecificWarningProperties(parentProjectSpec), + parentProjectSpec.TargetFrameworks.Select(f => f.FrameworkName).AsList().AsReadOnly()); + + var parentPackageSpecifcNoWarn = ExtractPackageSpecificNoWarnPerFramework( + parentWarningProperties.PackageSpecificWarningProperties); + + var warningPropertiesCache = new Dictionary>( + StringComparer.OrdinalIgnoreCase); + + foreach (var targetGraph in targetGraphs) + { + if (string.IsNullOrEmpty(targetGraph.RuntimeIdentifier)) + { + if (parentPackageSpecifcNoWarn == null || + !parentPackageSpecifcNoWarn.TryGetValue(targetGraph.Framework, out var parentPackageSpecificNoWarnForFramework)) + { + parentPackageSpecificNoWarnForFramework = null; + } + + var transitiveNoWarnFromTargetGraph = ExtractTransitiveNoWarnProperties( + targetGraph, + parentProjectSpec.RestoreMetadata.ProjectName, + parentWarningProperties.ProjectWideWarningProperties.NoWarn.AsHashSet(), + parentPackageSpecificNoWarnForFramework, + warningPropertiesCache); + + projectFrameworks.Add(targetGraph.Framework); + + transitivePackageSpecificProperties = MergePackageSpecificWarningProperties( + transitivePackageSpecificProperties, + transitiveNoWarnFromTargetGraph); + } + } + + return new WarningPropertiesCollection( + projectWideWarningProperties: null, + packageSpecificWarningProperties: transitivePackageSpecificProperties, + projectFrameworks: projectFrameworks + ); + } + + /// + /// Traverses a Dependency grpah starting from the parent project in BF style. + /// + /// Parent project restore target graph. + /// File path of the parent project. + /// Project Wide NoWarn properties of the parent project. + /// Package Specific NoWarn properties of the parent project. + /// PackageSpecificWarningProperties containing all the NoWarn's for each package seen in the graph accumulated while traversing the graph. + private static PackageSpecificWarningProperties ExtractTransitiveNoWarnProperties( + RestoreTargetGraph targetGraph, + string parentProjectName, + HashSet parentProjectWideNoWarn, + Dictionary> parentPackageSpecificNoWarn, + Dictionary> warningPropertiesCache) + { + var dependencyMapping = new Dictionary(StringComparer.OrdinalIgnoreCase); + var queue = new Queue(); + var seen = new Dictionary(StringComparer.OrdinalIgnoreCase); + var resultWarningProperties = new PackageSpecificWarningProperties(); + var packageNoWarn = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + // All the packages in parent project's closure. + // Once we have collected data for all of these, we can exit. + var parentPackageDependencies = new HashSet(StringComparer.OrdinalIgnoreCase); + + var parentTargetFramework = targetGraph.Framework; + + // Add all dependencies into a dict for a quick transitive lookup + foreach (var dependencyGraphItem in targetGraph.Flattened) + { + WarningPropertiesCollection nodeWarningProperties = null; + HashSet nodeProjectWideNoWarn = null; + Dictionary> nodePackageSpecificNoWarn = null; + + if (IsProject(dependencyGraphItem.Key.Type)) + { + var localMatch = (LocalMatch)dependencyGraphItem.Data.Match; + var nodeProjectSpec = GetNodePackageSpec(localMatch); + var nearestFramework = nodeProjectSpec.GetTargetFramework(parentTargetFramework).FrameworkName; + + if (nearestFramework != null) + { + // Get the WarningPropertiesCollection from the PackageSpec + nodeWarningProperties = GetNodeWarningProperties(nodeProjectSpec, nearestFramework, warningPropertiesCache); + + nodeProjectWideNoWarn = nodeWarningProperties.ProjectWideWarningProperties.NoWarn.AsHashSet(); + + var nodePackageSpecificWarningProperties = ExtractPackageSpecificNoWarnForFramework( + nodeWarningProperties.PackageSpecificWarningProperties, + nearestFramework); + + if (nodePackageSpecificWarningProperties != null) + { + nodePackageSpecificNoWarn = nodePackageSpecificWarningProperties; + } + } + } + else + { + parentPackageDependencies.Add(dependencyGraphItem.Key.Name); + } + + var lookUpNode = new LookUpNode() + { + Dependencies = dependencyGraphItem.Data.Dependencies, + NodeWarningProperties = new NodeWarningProperties(nodeProjectWideNoWarn, nodePackageSpecificNoWarn) + }; + + dependencyMapping[dependencyGraphItem.Key.Name] = lookUpNode; + } + + // Get the direct dependencies for the parent project to seed the queue + var parentDependencies = dependencyMapping[parentProjectName]; + + // Seed the queue with the parent project's direct dependencies + AddDependenciesToQueue(parentDependencies.Dependencies, + queue, + parentProjectWideNoWarn, + parentPackageSpecificNoWarn); + + // Add the parent project to the seen set to prevent adding it back to the queue + AddToSeen(seen, new DependencyNode(id: parentProjectName, + isProject: true, + projectWideNoWarn: parentProjectWideNoWarn, + packageSpecificNoWarn: parentPackageSpecificNoWarn)); + + // start taking one node from the queue and get all of it's dependencies + while (queue.Count > 0) + { + var node = queue.Dequeue(); + + // Check if the node has already been visited, or if the node is a NoWarn superset of + // an existing node. If this is a superset it will not provide any new paths where a + // warning should be shown. + if (AddToSeen(seen, node) && dependencyMapping.TryGetValue(node.Id, out var nodeLookUp)) + { + var nodeId = node.Id; + var nodeIsProject = node.IsProject; + + var nodeDependencies = nodeLookUp.Dependencies; + var nodeWarningProperties = nodeLookUp.NodeWarningProperties; + + var nodeProjectWideNoWarn = nodeWarningProperties.ProjectWide; + var nodePackageSpecificNoWarn = nodeWarningProperties.PackageSpecific; + var pathWarningProperties = node.NodeWarningProperties; + var pathProjectWideNoWarn = pathWarningProperties.ProjectWide; + var pathPackageSpecificNoWarn = pathWarningProperties.PackageSpecific; + + // If the node is a project then we need to extract the warning properties and + // add those to the warning properties of the current path. + if (nodeIsProject) + { + // Merge the node's project wide no warn to the one in the path. + var mergedProjectWideNoWarn = MergeCodes(pathProjectWideNoWarn, nodeProjectWideNoWarn); + + // Merge the node's package specific no warn to the one in the path. + var mergedPackageSpecificNoWarn = MergePackageSpecificNoWarn(pathPackageSpecificNoWarn, nodePackageSpecificNoWarn); + + AddDependenciesToQueue(nodeDependencies, + queue, + mergedProjectWideNoWarn, + mergedPackageSpecificNoWarn); + + } + else if (parentPackageDependencies.Contains(nodeId)) + { + // Evaluate the current path for package properties + var packageNoWarnFromPath = ExtractPathNoWarnProperties(pathWarningProperties, nodeId); + if (packageNoWarn.TryGetValue(nodeId, out var noWarnCodes)) + { + // We have seen atleast one path which contained a NoWarn for the package + // We need to update the + noWarnCodes.IntersectWith(packageNoWarnFromPath); + } + else + { + noWarnCodes = packageNoWarnFromPath; + packageNoWarn.Add(nodeId, noWarnCodes); + } + + // Check if there was any NoWarn in the path + if (noWarnCodes.Count == 0) + { + // If the path does not "NoWarn" for this package then remove the path from parentPackageDependencies + // This is done because if there are no "NoWarn" in one path, the the warnings must come through + // We no longer care about this package in the graph + parentPackageDependencies.Remove(nodeId); + + // If parentPackageDependencies is empty then exit the graph traversal + if (parentPackageDependencies.Count == 0) + { + break; + } + } + + AddDependenciesToQueue(nodeDependencies, + queue, + pathWarningProperties.ProjectWide, + pathWarningProperties.PackageSpecific); + } + } + } + + // At the end of the graph traversal add the remaining package no warn lists into the result + foreach(var packageId in packageNoWarn.Keys) + { + resultWarningProperties.AddRangeOfCodes(packageNoWarn[packageId], packageId, parentTargetFramework); + } + + return resultWarningProperties; + } + + private static WarningPropertiesCollection GetNodeWarningProperties( + PackageSpec nodeProjectSpec, + NuGetFramework framework, + Dictionary> warningPropertiesCache) + { + var key = nodeProjectSpec.RestoreMetadata.ProjectPath; + + if (!warningPropertiesCache.TryGetValue(key, out var frameworkCollection)) + { + frameworkCollection + = new Dictionary(new NuGetFrameworkFullComparer()); + + warningPropertiesCache[key] = frameworkCollection; + } + + if (!frameworkCollection.TryGetValue(framework, out var collection)) + { + collection = new WarningPropertiesCollection( + nodeProjectSpec.RestoreMetadata?.ProjectWideWarningProperties, + PackageSpecificWarningProperties.CreatePackageSpecificWarningProperties(nodeProjectSpec, framework), + nodeProjectSpec.TargetFrameworks.Select(f => f.FrameworkName).AsList().AsReadOnly()); + + frameworkCollection.Add(framework, collection); + } + + return collection; + } + + /// + /// Add to the seen list for tracking. + /// + /// True if the node should be walked + private static bool AddToSeen(Dictionary seen, DependencyNode node) + { + var id = node.Id; + var nodeProps = node.NodeWarningProperties; + + if (!seen.TryGetValue(id, out var visitedProps)) + { + // New id + seen.Add(id, node.NodeWarningProperties); + return true; + } + if (!nodeProps.IsSubSetOf(visitedProps)) + { + // Find the intersection of properties between these nodes, + // these are the only properties that we need to look for in + // future passes. + seen[id] = nodeProps.GetIntersect(visitedProps); + return true; + } + + // This has already been walked + return false; + } + + private static void AddDependenciesToQueue(IEnumerable dependencies, + Queue queue, + HashSet projectWideNoWarn, + Dictionary> packageSpecificNoWarn) + { + // Add all the project's dependencies to the Queue with the merged WarningPropertiesCollection + foreach (var dependency in dependencies) + { + var queueNode = new DependencyNode( + dependency.Name, + IsProject(dependency.LibraryRange.TypeConstraint), + projectWideNoWarn, + packageSpecificNoWarn); + + // Add the metadata from the parent project here. + queue.Enqueue(queueNode); + } + } + + private static PackageSpec GetNodePackageSpec(LocalMatch localMatch) + { + return (PackageSpec) localMatch.LocalLibrary.Items[KnownLibraryProperties.PackageSpec]; + } + + /// + /// Extracts the no warn codes for a libraryId from the warning properties at the node in the graph. + /// + /// warning properties at the node in the graph. + /// libraryId for which the no warn codes have to be extracted. + /// HashSet of NuGetLogCodes containing the no warn codes for the libraryId. + public static HashSet ExtractPathNoWarnProperties( + NodeWarningProperties nodeWarningProperties, + string libraryId) + { + var result = new HashSet(); + if (nodeWarningProperties?.ProjectWide?.Count > 0) + { + result.UnionWith(nodeWarningProperties.ProjectWide); + } + + if (nodeWarningProperties?.PackageSpecific?.Count > 0 && + nodeWarningProperties.PackageSpecific.TryGetValue(libraryId, out var codes) && + codes?.Count > 0) + { + result.UnionWith(codes); + } + + return result; + } + + /// + /// Merge 2 WarningProperties objects. + /// This method will combine the warning properties from both the collections. + /// + /// First Object to be merged. + /// Second Object to be merged. + /// Returns a WarningProperties with the combined warning properties. + /// Returns the reference to one of the inputs if the other input is Null. + /// Returns a Null if both the input properties are Null. + public static HashSet MergeCodes( + HashSet first, + HashSet second) + { + HashSet result = null; + + if (TryMergeNullObjects(first, second, out var merged)) + { + result = merged; + } + else + { + if (first.Count == 0) + { + return second; + } + + if (second.Count == 0) + { + return first; + } + + if (first.SetEqualsWithNullCheck(second)) + { + return first; + } + + // Merge NoWarn Sets. + result = new HashSet(first.Concat(second)); + } + + return result; + } + + /// + /// Merge 2 PackageSpecific NoWarns. + /// This method will combine the warning properties from both the collections. + /// + /// First Object to be merged. + /// Second Object to be merged. + /// Returns a PackageSpecificWarningProperties with the combined warning properties. + /// Will return the reference to one of the inputs if the other input is Null. + /// Returns a Null if both the input properties are Null. + public static Dictionary> MergePackageSpecificNoWarn( + Dictionary> first, + Dictionary> second) + { + if (TryMergeNullObjects(first, second, out var merged)) + { + return merged; + } + + if (first.Count == 0) + { + return second; + } + + if (second.Count == 0) + { + return first; + } + + merged = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + foreach (var pair in first.Concat(second)) + { + var id = pair.Key; + + if (!merged.TryGetValue(id, out var codes)) + { + // Did not exist, use the existing code set + merged.Add(id, pair.Value); + } + else + { + // Create a new set with the merged codes + merged[id] = MergeCodes(codes, pair.Value); + } + } + + return merged; + } + + /// + /// Merge 2 PackageSpecificWarningProperties objects. + /// This method will combine the warning properties from both the collections. + /// + /// First Object to be merged. + /// Second Object to be merged. + /// Returns a PackageSpecificWarningProperties with the combined warning properties. + /// Will return the reference to one of the inputs if the other input is Null. + /// Returns a Null if both the input properties are Null. + public static PackageSpecificWarningProperties MergePackageSpecificWarningProperties( + PackageSpecificWarningProperties first, + PackageSpecificWarningProperties second) + { + PackageSpecificWarningProperties result = null; + + if (TryMergeNullObjects(first, second, out var merged)) + { + result = merged; + } + else + { + result = new PackageSpecificWarningProperties(); + if (first.Properties != null) + { + foreach (var codePair in first.Properties) + { + var code = codePair.Key; + var libraryCollection = codePair.Value; + + foreach (var libraryPair in libraryCollection) + { + var libraryId = libraryPair.Key; + var frameworks = libraryPair.Value; + + result.AddRangeOfFrameworks(code, libraryId, frameworks); + } + } + } + + if (second.Properties != null) + { + foreach (var codePair in second.Properties) + { + var code = codePair.Key; + var libraryCollection = codePair.Value; + + foreach (var libraryPair in libraryCollection) + { + var libraryId = libraryPair.Key; + var frameworks = libraryPair.Value; + + result.AddRangeOfFrameworks(code, libraryId, frameworks); + } + } + } + } + + return result; + } + + /// + /// Try to merge 2 objects if one or both of them are null. + /// + /// First Object to be merged. + /// Second Object to be merged. + /// Out Merged Object. + /// Returns true if atleast one of the objects was Null. + /// If none of them is null then the returns false, indicating that the merge failed. + public static bool TryMergeNullObjects(T first, T second, out T merged) where T : class + { + merged = null; + var result = false; + + if (first == null && second == null) + { + merged = null; + result = true; + } + else if (first == null) + { + merged = second; + result = true; + } + else if (second == null) + { + merged = first; + result = true; + } + + return result; + } + + /// + /// Checks if a LibraryDependencyTarget is a project. + /// + /// LibraryDependencyTarget to be checked. + /// True if a LibraryDependencyTarget is Project or ExternalProject. + private static bool IsProject(LibraryDependencyTarget type) + { + return (type == LibraryDependencyTarget.ExternalProject || type == LibraryDependencyTarget.Project); + } + + /// + /// Checks if a LibraryType is a project. + /// + /// LibraryType to be checked. + /// True if a LibraryType is Project or ExternalProject. + private static bool IsProject(LibraryType type) + { + return (type == LibraryType.ExternalProject || type == LibraryType.Project); + } + + /// + /// Indexes a PackageSpecificWarningProperties collection on framework. + /// + /// PackageSpecificWarningProperties to be converted. + /// New dictionary containing the data of a PackageSpecificWarningProperties collection on framework. + public static Dictionary>> ExtractPackageSpecificNoWarnPerFramework( + PackageSpecificWarningProperties packageSpecificWarningProperties) + { + Dictionary>> result = null; + + if (packageSpecificWarningProperties?.Properties != null) + { + result = new Dictionary>>(new NuGetFrameworkFullComparer()); + + foreach (var codePair in packageSpecificWarningProperties.Properties) + { + var code = codePair.Key; + var libraryCollection = codePair.Value; + + foreach (var libraryPair in libraryCollection) + { + var libraryId = libraryPair.Key; + var frameworks = libraryPair.Value; + + foreach (var framework in frameworks) + { + if (!result.TryGetValue(framework, out var frameworkCollection)) + { + frameworkCollection = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + result[framework] = frameworkCollection; + } + + if (!frameworkCollection.TryGetValue(libraryId, out var codes)) + { + codes = new HashSet(); + frameworkCollection[libraryId] = codes; + } + + codes.Add(code); + } + } + } + } + return result; + } + + /// + /// Indexes a PackageSpecificWarningProperties collection on framework. + /// + /// PackageSpecificWarningProperties to be converted. + /// New dictionary containing the data of a PackageSpecificWarningProperties collection on framework. + public static Dictionary> ExtractPackageSpecificNoWarnForFramework( + PackageSpecificWarningProperties packageSpecificWarningProperties, + NuGetFramework framework) + { + Dictionary> result = null; + + if (packageSpecificWarningProperties?.Properties != null && framework != null) + { + result = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + foreach (var codePair in packageSpecificWarningProperties.Properties) + { + var code = codePair.Key; + var libraryCollection = codePair.Value; + + foreach (var libraryPair in libraryCollection) + { + var libraryId = libraryPair.Key; + var frameworks = libraryPair.Value; + + if (frameworks.Contains(framework)) + { + if (!result.TryGetValue(libraryId, out var codes)) + { + codes = new HashSet(); + result[libraryId] = codes; + } + codes.Add(code); + } + } + } + } + + return result; + } + + /// + /// A simple node class to hold the outgoing dependency edge during the graph walk. + /// + public class DependencyNode : IEquatable + { + // ID of the Node + public string Id { get; } + + // bool to indicate if the node is a project node + // if false then the node is a package + public bool IsProject { get; } + + // If a node is a project then it will hold these properties + public NodeWarningProperties NodeWarningProperties { get; } + + public DependencyNode(string id, bool isProject, HashSet projectWideNoWarn, Dictionary> packageSpecificNoWarn) + { + Id = id ?? throw new ArgumentNullException(nameof(id)); + NodeWarningProperties = new NodeWarningProperties(projectWideNoWarn, packageSpecificNoWarn); + IsProject = isProject; + } + + public DependencyNode(string id, bool isProject, NodeWarningProperties nodeWarningProperties) + { + Id = id ?? throw new ArgumentNullException(nameof(id)); + NodeWarningProperties = nodeWarningProperties ?? throw new ArgumentNullException(nameof(nodeWarningProperties)); + IsProject = isProject; + } + + public override int GetHashCode() + { + var hashCode = new HashCodeCombiner(); + + hashCode.AddStringIgnoreCase(Id); + hashCode.AddObject(IsProject); + + return hashCode.CombinedHash; + } + + public override bool Equals(object obj) + { + return Equals(obj as DependencyNode); + } + + public bool Equals(DependencyNode other) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return IsProject == other.IsProject && + string.Equals(Id, other.Id, StringComparison.OrdinalIgnoreCase) && + EqualityUtility.EqualsWithNullCheck(NodeWarningProperties, other.NodeWarningProperties); + } + + public override string ToString() + { + return $"{(IsProject ? "Project" : "Package")}/{Id}"; + } + } + + /// + /// A simple node class to hold the outgoing dependency edges for a quick look up. + /// + private class LookUpNode + { + // List of dependencies for this node + public IEnumerable Dependencies { get; set; } + + // If a node is a project then it will hold these properties + public NodeWarningProperties NodeWarningProperties { get; set; } + + } + + + /// + /// A class to hold minimal version of project wide nowarn and package specific no warn for a project. + /// + public class NodeWarningProperties : IEquatable + { + // ProjectWide NoWarn properties + public HashSet ProjectWide { get; } + + // PackageSpecific NoWarn + // We do not use framework here as DependencyNode is created per parent project framework. + public Dictionary> PackageSpecific { get; } + + + public NodeWarningProperties( + HashSet projectWide, + Dictionary> packageSpecific) + { + ProjectWide = projectWide; + PackageSpecific = packageSpecific; + } + + public override int GetHashCode() + { + var hashCode = new HashCodeCombiner(); + + hashCode.AddSequence(ProjectWide); + hashCode.AddDictionary(PackageSpecific); + + return hashCode.CombinedHash; + } + + public override bool Equals(object obj) + { + return Equals(obj as NodeWarningProperties); + } + + + public bool Equals(NodeWarningProperties other) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return EqualityUtility.SetEqualsWithNullCheck(ProjectWide, other.ProjectWide) && + EqualityUtility.DictionaryEquals(PackageSpecific, other.PackageSpecific, (s, o) => EqualityUtility.SetEqualsWithNullCheck(s, o)); + } + + public NodeWarningProperties GetIntersect(NodeWarningProperties other) + { + if (other == null || ReferenceEquals(this, other)) + { + return new NodeWarningProperties(ProjectWide, PackageSpecific); + } + + var thisPackages = PackageSpecific; + var otherPackages = other.PackageSpecific; + var projectWide = Intersect(ProjectWide, other.ProjectWide); + Dictionary> packages = null; + + if (thisPackages != null && otherPackages == null) + { + packages = thisPackages; + } + else if (thisPackages == null && otherPackages != null) + { + packages = otherPackages; + } + else if (thisPackages != null && otherPackages != null) + { + packages = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + var allKeys = thisPackages.Keys.Concat(otherPackages.Keys) + .Distinct(StringComparer.OrdinalIgnoreCase); + + foreach (var key in allKeys) + { + thisPackages.TryGetValue(key, out var thisCodes); + otherPackages.TryGetValue(key, out var otherCodes); + + packages.Add(key, Intersect(thisCodes, otherCodes)); + } + } + + return new NodeWarningProperties(projectWide, packages); + } + + /// + /// True if the given set is a subset of this set, or equal to it. + /// + /// Null is considered an empty set, and will return true. + public bool IsSubSetOf(NodeWarningProperties other) + { + if (other == null) + { + return true; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (IsSubSetOfWithNullCheck(ProjectWide, other.ProjectWide)) + { + var package = PackageSpecific; + var otherPackage = other.PackageSpecific; + + if (otherPackage == null || otherPackage.Count == 0) + { + return true; + } + + if (package == null || package.Count == 0) + { + return false; + } + + if (otherPackage.Count <= package.Count) + { + // To be a subset this set of package specific warnings must contain + // every id and code found in other. A single miss will fail the check. + foreach (var pair in otherPackage) + { + if (!package.TryGetValue(pair.Key, out var codes) + || !codes.IsSubsetOf(pair.Value)) + { + return false; + } + } + } + + return true; + } + + return false; + } + + private static bool IsSubSetOfWithNullCheck(HashSet parent, HashSet other) + { + // Null is empty and always a subset. + if (other == null || other.Count == 0) + { + return true; + } + + // A null or empty parent cannot be a superset. + if (parent == null || parent.Count == 0) + { + return false; + } + + if (other.Count <= parent.Count) + { + return parent.IsSubsetOf(other); + } + + return false; + } + + private static HashSet Intersect(HashSet first, HashSet second) + { + if (ReferenceEquals(first, second)) + { + return first; + } + + if (first == null) + { + return second; + } + + if (second == null) + { + return first; + } + + var result = new HashSet(first); + result.IntersectWith(second); + + return result; + } + } + } +} diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/WarningPropertiesCollection.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/WarningPropertiesCollection.cs index 1d4327fb994..bad359bc1cd 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/WarningPropertiesCollection.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Logging/WarningPropertiesCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -16,7 +16,7 @@ namespace NuGet.Commands /// /// Class to hold ProjectWide and PackageSpecific WarningProperties. /// - public class WarningPropertiesCollection + public class WarningPropertiesCollection : IEquatable { private readonly ConcurrentDictionary _getFrameworkCache = new ConcurrentDictionary(); @@ -24,18 +24,27 @@ public class WarningPropertiesCollection /// Contains the target frameworks for the project. /// These are used for no warn filtering in case of a log message without a target graph. /// - public IReadOnlyList ProjectFrameworks { get; set; } = new ReadOnlyCollection(new List()); + public IReadOnlyList ProjectFrameworks { get; } /// /// Contains Project wide properties for Warnings. /// - public WarningProperties ProjectWideWarningProperties { get; set; } + public WarningProperties ProjectWideWarningProperties { get; } /// /// Contains Package specific properties for Warnings. /// NuGetLogCode -> LibraryId -> Set of Frameworks. /// - public PackageSpecificWarningProperties PackageSpecificWarningProperties { get; set; } + public PackageSpecificWarningProperties PackageSpecificWarningProperties { get; } + + public WarningPropertiesCollection(WarningProperties projectWideWarningProperties, + PackageSpecificWarningProperties packageSpecificWarningProperties, + IReadOnlyList projectFrameworks) + { + ProjectWideWarningProperties = projectWideWarningProperties; + PackageSpecificWarningProperties = packageSpecificWarningProperties; + ProjectFrameworks = projectFrameworks ?? new ReadOnlyCollection(new List()); + } /// /// Attempts to suppress a warning log message or upgrade it to error log message. @@ -46,75 +55,116 @@ public class WarningPropertiesCollection /// If not then the param message sould have been mutated to an error public bool ApplyWarningProperties(IRestoreLogMessage message) { - if (message.Level != LogLevel.Warning) + if (ApplyProjectWideNoWarnProperties(message) || ApplyPackageSpecificNoWarnProperties(message)) { - return false; + return true; } else { - // First look at PackageSpecificWarningProperties and then at ProjectWideWarningProperties. - if (!string.IsNullOrEmpty(message.LibraryId) && PackageSpecificWarningProperties != null) - { - var messageTargetFrameworks = message.TargetGraphs.Select(GetNuGetFramework).ToList(); + ApplyWarningAsErrorProperties(message); + return false; + } + } + + /// + /// Attempts to suppress a warning log message. + /// The decision is made based on the Package Specific or Project wide no warn properties. + /// + /// Message that should be suppressed. + /// Bool indicating is the warning should be suppressed or not. + public bool ApplyNoWarnProperties(IRestoreLogMessage message) + { + return ApplyProjectWideNoWarnProperties(message) || ApplyPackageSpecificNoWarnProperties(message); + } + + /// + /// Method is used to upgrade a warning to an error if needed. + /// + /// Message which should be upgraded to error if needed. + public void ApplyWarningAsErrorProperties(IRestoreLogMessage message) + { + ApplyProjectWideWarningsAsErrorProperties(message); + } + + /// + /// Method is used to check is a warning should be suppressed due to package specific no warn properties. + /// + /// Message to be checked for no warn. + /// bool indicating if the IRestoreLogMessage should be suppressed or not. + private bool ApplyPackageSpecificNoWarnProperties(IRestoreLogMessage message) + { + if (message.Level == LogLevel.Warning && + PackageSpecificWarningProperties != null && + !string.IsNullOrEmpty(message.LibraryId)) + { + var messageTargetFrameworks = message.TargetGraphs.Select(GetNuGetFramework).ToList(); - // If the message does not contain a target graph, assume that it is applicable for all project frameworks. - if (messageTargetFrameworks.Count == 0) + // If the message does not contain a target graph, assume that it is applicable for all project frameworks. + if (messageTargetFrameworks.Count == 0) + { + // Suppress the warning if the code + libraryId combination is suppressed for all project frameworks. + if (ProjectFrameworks.Count > 0 && + ProjectFrameworks.All(e => PackageSpecificWarningProperties.Contains(message.Code, message.LibraryId, e))) { - // Suppress the warning if the code + libraryId combination is suppressed for all project frameworks. - if (ProjectFrameworks.Count > 0 && - ProjectFrameworks.All(e => PackageSpecificWarningProperties.Contains(message.Code, message.LibraryId, e))) - { - return true; - } + return true; } - else + } + else + { + // Get all the target graphs for which code + libraryId combination is not suppressed. + message.TargetGraphs = message + .TargetGraphs + .Where(e => !PackageSpecificWarningProperties.Contains(message.Code, message.LibraryId, GetNuGetFramework(e))) + .ToList(); + + // If the message is left with no target graphs then suppress it. + if (message.TargetGraphs.Count == 0) { - // Get all the target graphs for which code + libraryId combination is not suppressed. - message.TargetGraphs = message - .TargetGraphs - .Where(e => !PackageSpecificWarningProperties.Contains(message.Code, message.LibraryId, GetNuGetFramework(e))) - .ToList(); - - // If the message is left with no target graphs then suppress it. - if (message.TargetGraphs.Count == 0) - { - return true; - } + return true; } } - - // The message does not contain a LibraryId or it is not suppressed in package specific settings. - // Apply ProjectWideWarningProperties. - return ProjectWideWarningProperties != null && ApplyProjectWideWarningProperties(message); } + + // The message is not a warning or it does not contain a LibraryId or it is not suppressed in package specific settings. + return false; } /// - /// Method is used to check is a warning should be suppressed and if not then if it should be treated as an error. + /// Method is used to check is a warning should be suppressed due to project wide no warn properties. /// - /// Message which should be mutated if needed. + /// Message to be checked for no warn. /// bool indicating if the ILogMessage should be suppressed or not. - private bool ApplyProjectWideWarningProperties(ILogMessage logMessage) + private bool ApplyProjectWideNoWarnProperties(ILogMessage message) { - if (logMessage.Level == LogLevel.Warning) + if (message.Level == LogLevel.Warning && ProjectWideWarningProperties != null) { - if (ProjectWideWarningProperties.NoWarn.Contains(logMessage.Code)) + if (ProjectWideWarningProperties.NoWarn.Contains(message.Code)) { // If the project wide NoWarn contains the message code then suppress it. return true; - } - else if ((ProjectWideWarningProperties.AllWarningsAsErrors && logMessage.Code > NuGetLogCode.Undefined) || - ProjectWideWarningProperties.WarningsAsErrors.Contains(logMessage.Code)) + } + } + + // the project wide NoWarn does contain the message code. do not suppress the warning. + return false; + } + + /// + /// Method is used to check is a warning should be treated as an error. + /// + /// Message which should be upgraded to error if needed. + private void ApplyProjectWideWarningsAsErrorProperties(ILogMessage message) + { + if (message.Level == LogLevel.Warning && ProjectWideWarningProperties != null) + { + if ((ProjectWideWarningProperties.AllWarningsAsErrors && message.Code > NuGetLogCode.Undefined) || + ProjectWideWarningProperties.WarningsAsErrors.Contains(message.Code)) { // If the project wide AllWarningsAsErrors is true and the message has a valid code or - // project wide WarningsAsErrors contains the message code then pgrade to error and do not suppress it. - logMessage.Level = LogLevel.Error; - return false; + // Project wide WarningsAsErrors contains the message code then upgrade to error. + message.Level = LogLevel.Error; } } - - // Finally do not suppress or modify the message. - return false; } private NuGetFramework GetNuGetFramework(string targetGraph) @@ -128,5 +178,38 @@ private static NuGetFramework GetNuGetFrameworkFromTargetGraph(string targetGrap return NuGetFramework.Parse(parts[0]); } + + public override int GetHashCode() + { + var hashCode = new HashCodeCombiner(); + + hashCode.AddObject(ProjectWideWarningProperties); + hashCode.AddObject(PackageSpecificWarningProperties); + hashCode.AddSequence(ProjectFrameworks); + + return hashCode.CombinedHash; + } + + public override bool Equals(object obj) + { + return Equals(obj as WarningPropertiesCollection); + } + + public bool Equals(WarningPropertiesCollection other) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return EqualityUtility.EqualsWithNullCheck(ProjectWideWarningProperties, other.ProjectWideWarningProperties) && + EqualityUtility.EqualsWithNullCheck(PackageSpecificWarningProperties, other.PackageSpecificWarningProperties) && + EqualityUtility.OrderedEquals(ProjectFrameworks, other.ProjectFrameworks, (fx) => fx.Framework, orderComparer: StringComparer.OrdinalIgnoreCase, sequenceComparer: new NuGetFrameworkFullComparer()); + } } } diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreCommand.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreCommand.cs index eb99abdd92c..055487fc03b 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreCommand.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -22,7 +22,7 @@ namespace NuGet.Commands { internal class ProjectRestoreCommand { - private readonly ILogger _logger; + private readonly RestoreCollectorLogger _logger; private readonly ProjectRestoreRequest _request; @@ -32,7 +32,7 @@ public ProjectRestoreCommand(ProjectRestoreRequest request) _request = request; } - public async Task, RuntimeGraph>> TryRestore(LibraryRange projectRange, + public async Task, RuntimeGraph>> TryRestoreAsync(LibraryRange projectRange, IEnumerable frameworkRuntimePairs, HashSet allInstalledPackages, NuGetv3LocalRepository userPackageFolder, @@ -119,6 +119,10 @@ await InstallPackagesAsync(runtimeGraphs, userPackageFolder.ClearCacheForIds(allInstalledPackages.Select(package => package.Name)); } + // Update the logger with the restore target graphs + // This allows lazy initialization for the Transitive Warning Properties + _logger.ApplyRestoreOutput(graphs); + // Warn for all dependencies that do not have exact matches or // versions that have been bumped up unexpectedly. await UnexpectedDependencyMessages.LogAsync(graphs, _request.Project, _logger); diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreRequest.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreRequest.cs index 4904043172b..f4387524927 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreRequest.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/ProjectRestoreRequest.cs @@ -21,7 +21,7 @@ public ProjectRestoreRequest( LockFile existingLockFile, Dictionary runtimeGraphCache, ConcurrentDictionary runtimeGraphCacheByPackage, - ILogger log) + RestoreCollectorLogger log) { CacheContext = request.CacheContext; Log = log; @@ -36,7 +36,7 @@ public ProjectRestoreRequest( } public SourceCacheContext CacheContext { get; } - public ILogger Log { get; } + public RestoreCollectorLogger Log { get; } public string PackagesDirectory { get; } public int MaxDegreeOfConcurrency { get; } public LockFile ExistingLockFile { get; } diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs index 51926959495..6ec31ce916c 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs @@ -53,16 +53,9 @@ public RestoreCommand(RestoreRequest request) var collectorLoggerHideWarningsAndErrors = request.Project.RestoreSettings.HideWarningsAndErrors || request.HideWarningsAndErrors; - var collectorLogger = new RestoreCollectorLogger(_request.Log, collectorLoggerHideWarningsAndErrors) - { - ProjectPath = _request.Project.RestoreMetadata?.ProjectPath, - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = request.Project.RestoreMetadata?.ProjectWideWarningProperties, - PackageSpecificWarningProperties = PackageSpecificWarningProperties.CreatePackageSpecificWarningProperties(request.Project), - ProjectFrameworks = request.Project.TargetFrameworks.Select(f => f.FrameworkName).AsList().AsReadOnly() - } - }; + var collectorLogger = new RestoreCollectorLogger(_request.Log, collectorLoggerHideWarningsAndErrors); + + collectorLogger.ApplyRestoreInputs(_request.Project); _logger = collectorLogger; } @@ -568,7 +561,7 @@ private async Task> ExecuteRestoreAsync( var projectRestoreCommand = new ProjectRestoreCommand(projectRestoreRequest); - var result = await projectRestoreCommand.TryRestore( + var result = await projectRestoreCommand.TryRestoreAsync( projectRange, projectFrameworkRuntimePairs, allInstalledPackages, @@ -615,7 +608,7 @@ private async Task> ExecuteRestoreAsync( // Walk additional runtime graphs for supports checks if (_success && _request.CompatibilityProfiles.Any()) { - var compatibilityResult = await projectRestoreCommand.TryRestore( + var compatibilityResult = await projectRestoreCommand.TryRestoreAsync( projectRange, _request.CompatibilityProfiles, allInstalledPackages, diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreSummary.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreSummary.cs index 2328200407b..325c2e3b510 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreSummary.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreSummary.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; diff --git a/src/NuGet.Core/NuGet.ProjectModel/WarningProperties.cs b/src/NuGet.Core/NuGet.ProjectModel/WarningProperties.cs index a5ab2b1c246..2e3a8717ac5 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/WarningProperties.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/WarningProperties.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -16,21 +16,24 @@ public class WarningProperties : IEquatable /// /// List of Warning Codes that should be treated as Errors. /// - public ISet WarningsAsErrors { get; } = new HashSet(); + public ISet WarningsAsErrors { get; } /// /// List of Warning Codes that should be ignored. /// - public ISet NoWarn { get; } = new HashSet(); + public ISet NoWarn { get; } /// /// Indicates if all warnings should be ignored. /// - public bool AllWarningsAsErrors { get; set; } = false; + public bool AllWarningsAsErrors { get; set; } public WarningProperties() { - } + WarningsAsErrors = new HashSet(); + NoWarn = new HashSet(); + AllWarningsAsErrors = false; + } public WarningProperties(ISet warningsAsErrors, ISet noWarn, bool allWarningsAsErrors) : base() @@ -44,7 +47,6 @@ public override int GetHashCode() { var hashCode = new HashCodeCombiner(); - hashCode.AddObject(AllWarningsAsErrors); hashCode.AddSequence(WarningsAsErrors); hashCode.AddSequence(NoWarn); @@ -69,8 +71,8 @@ public bool Equals(WarningProperties other) } return AllWarningsAsErrors == other.AllWarningsAsErrors && - WarningsAsErrors.SetEquals(other.WarningsAsErrors) && - NoWarn.SetEquals(other.NoWarn); + EqualityUtility.SetEqualsWithNullCheck(WarningsAsErrors, other.WarningsAsErrors) && + EqualityUtility.SetEqualsWithNullCheck(NoWarn, other.NoWarn); } } } diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreTransitiveLoggingTests.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreTransitiveLoggingTests.cs new file mode 100644 index 00000000000..d2f8d54f7f6 --- /dev/null +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreTransitiveLoggingTests.cs @@ -0,0 +1,2429 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using FluentAssertions; +using NuGet.Frameworks; +using NuGet.Packaging.Core; +using NuGet.Test.Utility; +using Test.Utility; +using Xunit; + +namespace NuGet.CommandLine.Test +{ + public class RestoreTransitiveLoggingTests + { + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY -> PkgZ v 1.0.1 + // -> PkgZ v 1.0.0 + public void GivenAProjectReferenceDoesNotNoWarnForAllWarningsVerifyWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced indirectly and Created in the source + var packageZ11 = new SimpleTestPackageContext() + { + Id = "z", + Version = "1.0.1" + }; + + // Referenced directly and Created in the source + var packageZ = new SimpleTestPackageContext() + { + Id = "z", + Version = "1.0.0" + }; + + // Referenced directly and Created in the source + var packageY = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + packageY.Dependencies.Add(packageZ11); + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageZ); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageZ11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY); + + // B -> X + // B -> Y -> Z v1.0.1 + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(packageY); + projectB.AddPackageToAllFrameworks(packageZ); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().Contain("NU1605"); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[TreatWarningsAsErrors true] -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY -> PkgZ v 1.0.1 + // -> PkgZ v 1.0.0 + public void GivenAProjectReferenceDoesNotNoWarnForAllWarningsAndDirectTreatWarningsAsErrorsVerifyError() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced indirectly and Created in the source + var packageZ11 = new SimpleTestPackageContext() + { + Id = "z", + Version = "1.0.1" + }; + + // Referenced directly and Created in the source + var packageZ = new SimpleTestPackageContext() + { + Id = "z", + Version = "1.0.0" + }; + + // Referenced directly and Created in the source + var packageY = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + packageY.Dependencies.Add(packageZ11); + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageZ); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageZ11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY); + + // B -> X + // B -> Y -> Z v1.0.1 + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(packageY); + projectB.AddPackageToAllFrameworks(packageZ); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("TreatWarningsAsErrors", "true"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 1); + + // Assert + r.Success.Should().BeFalse(); + r.AllOutput.Should().Contain("NU1605"); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[WarningsAsErrors NU1605, NU1603] -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY -> PkgZ v 1.0.1 + // -> PkgZ v 1.0.0 + public void GivenAProjectReferenceDoesNotNoWarnForAllWarningsAndDirectWarningsAsErrorsVerifyError() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced indirectly and Created in the source + var packageZ11 = new SimpleTestPackageContext() + { + Id = "z", + Version = "1.0.1" + }; + + // Referenced directly and Created in the source + var packageZ = new SimpleTestPackageContext() + { + Id = "z", + Version = "1.0.0" + }; + + // Referenced directly and Created in the source + var packageY = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + packageY.Dependencies.Add(packageZ11); + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageZ); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageZ11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY); + + // B -> X + // B -> Y -> Z v1.0.1 + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(packageY); + projectB.AddPackageToAllFrameworks(packageZ); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("WarningsAsErrors", "NU1603; NU1605"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 1); + + // Assert + r.Success.Should().BeFalse(); + r.AllOutput.Should().Contain("NU1605"); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[WarnAsError NU1603] -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceNoWarnsAndDirectWarnAsErrorVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("WarningsAsErrors", "NU1603; NU1701"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[TreatWarningsAsError true] -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceNoWarnsAndDirectTreatWarningsAsErrorVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("TreatWarningsAsErrors", "true"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[WarnAsError NU1603] -> ProjB -> PkgX[NU1603] + public void GivenAProjectReferenceDoesNotNoWarnAndDirectWarnAsErrorVerifyError() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("WarningsAsErrors", "NU1603; NU1701"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 1); + + // Assert + r.Success.Should().BeFalse(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + [Fact] + // Tests ProjA[TreatWarningsAsError true] -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY[NU1603] + public void GivenAProjectReferenceDoesNotNoWarnForAllReferencesAndDirectTreatWarningsAsErrorVerifyError() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced but not created + var packageY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.0" + }; + + // Created in the source + var packageY11 = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(packageY); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("TreatWarningsAsErrors", "true"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 1); + + // Assert + r.Success.Should().BeFalse(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + [Fact] + // Tests ProjA[TreatWarningsAsError true] -> ProjB[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY[NU1603] + public void GivenAProjectReferenceNoWarnsForAllReferencesAndDirectTreatWarningsAsErrorVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced but not created + var packageY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.0" + }; + + // Created in the source + var packageY11 = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(packageY); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("TreatWarningsAsErrors", "true"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[TreatWarningsAsError true] -> ProjB -> PkgX[NU1603] + public void GivenAProjectReferenceDoesNotNoWarnAndDirectTreatWarningsAsErrorVerifyError() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Properties.Add("TreatWarningsAsErrors", "true"); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 1); + + // Assert + r.Success.Should().BeFalse(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY[NU1603] + public void GivenAProjectReferenceNoWarnsVerifyWarningForDirectReference() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced but not created + var packageY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.0" + }; + + // Created in the source + var packageY11 = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + // A -> Y + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddPackageToAllFrameworks(packageY); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + GetSubstringCount(r.AllOutput, "NU1603", ignoreCase: false).Should().Be(1); + } + } + + [Fact] + // Tests ProjA[PkgY NoWarn NU1603] -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgY[NU1603] + public void GivenAProjectReferenceNoWarnsAndDirectReferenceNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced but not created + var packageY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageY11 = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageY11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + // A -> Y + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddPackageToAllFrameworks(packageY); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[net461] -> ProjB[netstandard2.0][PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceWithFallBackFrameworkNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("net461")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netstandard2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[net45] -> ProjB[net461][PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceWithIncompatibleFrameworkNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("net45")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("net461")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 1); + + // Assert + r.Success.Should().BeFalse(); + r.AllOutput.Should().Contain("NU1201"); + } + } + + [Fact] + // Tests ProjA[net461] -> ProjB[netstandard2.0][ProjectWide NoWarn NU1603] -> PkgX[NU1603] + // -> ToolY[NU1603] + public void GivenAProjectReferenceWithToolAndProjectWideNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("net461")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netstandard2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + }; + + // Created in the source + var packageX101 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced but not created + var toolY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1", + PackageType = PackageType.DotnetCliTool + }; + + // Created in the source + var toolY101 = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1", + PackageType = PackageType.DotnetCliTool + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX101); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, toolY101); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(toolY); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[net461] -> ProjB[netstandard2.0][PkgX, ToolY NoWarn NU1603] -> PkgX[NU1603] + // -> ToolY[NU1603] + public void GivenAProjectReferenceWithToolAndPackageSpecificNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("net461")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netstandard2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX101 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Referenced but not created + var toolY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1", + PackageType = PackageType.DotnetCliTool, + NoWarn = "NU1603" + }; + + // Created in the source + var toolY101 = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1", + PackageType = PackageType.DotnetCliTool + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX101); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, toolY101); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.AddPackageToAllFrameworks(toolY); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA[net461] -> ProjB[netstandard2.0][ProjectWide NoWarn NU1603] -> ToolY -> PkgX[NU1603] + public void GivenAProjectReferenceWithToolBringingTransitivePackageNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("net461")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netstandard2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + }; + + // Created in the source + var packageX101 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + // Created in the source + var toolY = new SimpleTestPackageContext() + { + Id = "y", + Version = "1.0.1", + PackageType = PackageType.DotnetCliTool, + Dependencies = new List { packageX } + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX101); + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, toolY); + + // B -> X + projectB.AddPackageToAllFrameworks(toolY); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceNoWarnsProjectWideVerifyNoWarning() + { + + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenAProjectReferenceWithDifferentFrameworkNoWarnsVerifyNoWarning() + { + + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp1.1")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB -> ProjC[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenATransitiveProjectReferenceNoWarnsVerifyNoWarning() + { + + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // C -> X + projectC.AddPackageToAllFrameworks(packageX); + projectC.Save(); + + // B -> C + projectB.AddProjectToAllFrameworks(projectC); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB -> ProjC[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + public void GivenATransitiveProjectReferenceNoWarnsProjectWideVerifyNoWarning() + { + + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // C -> X + projectC.AddPackageToAllFrameworks(packageX); + projectC.Properties.Add("NoWarn", "NU1603"); + projectC.Save(); + + // B -> C + projectB.AddProjectToAllFrameworks(projectC); + projectB.Save(); + + // A -> B + projectA.AddProjectToAllFrameworks(projectB); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgX[NU1603] + public void GivenAProjectReferenceNoWarnsButDirectReferenceGeneratesWarningVerifyWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageXWithNoWarn); + projectB.Save(); + + // A -> B + // A -> X + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddPackageToAllFrameworks(packageX); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + // -> PkgX[NU1603] + public void GivenAProjectReferenceNoWarnsProjectWideButDirectReferenceGeneratesWarningVerifyWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // A -> B + // A -> X + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddPackageToAllFrameworks(packageX); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenMultipleProjectReferencesNoWarnVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageXWithNoWarn); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageXWithNoWarn); + projectC.Save(); + + // A -> B + // A -> C + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + public void GivenMultipleProjectReferencesNoWarnProjectWideVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageX); + projectC.Properties.Add("NoWarn", "NU1603"); + projectC.Save(); + + // A -> B + // A -> C + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC[ProjectWide NoWarn NU1605] -> PkgX[NU1603] + public void GivenMultipleProjectReferencesNoWarnDifferentWarningsProjectWideVerifyWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageX); + projectC.Properties.Add("NoWarn", "NU1605"); + projectC.Save(); + + // A -> B + // A -> C + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + GetSubstringCount(r.AllOutput, "NU1603", ignoreCase: false).Should().Be(3); + } + } + + [Fact] + // Tests ProjA -> ProjB[ProjectWide NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenMultipleProjectReferencesNoWarnMixedVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Properties.Add("NoWarn", "NU1603"); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageXWithNoWarn); + projectC.Save(); + + // A -> B + // A -> C + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC -> PkgX[NU1603] + public void GivenMultipleProjectReferencesAndOnePathWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageXWithNoWarn); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageX); + projectC.Save(); + + // A -> B + // A -> C + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> PkgX[NU1603] + public void GivenMultipleProjectReferencesNoWarnButDirectReferenceWarnsVerifyWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageXWithNoWarn); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageXWithNoWarn); + projectC.Save(); + + // A -> B + // A -> C + // A -> X + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.AddPackageToAllFrameworks(packageX); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + GetSubstringCount(r.AllOutput, "NU1603", ignoreCase: false).Should().Be(1); + } + } + + [Fact] + // Tests ProjA[PkgX NoWarn NU1603] -> ProjB -> PkgX[NU1603] + // -> ProjC -> PkgX[NU1603] + // -> PkgX[NU1603] + public void GivenMultipleProjectReferencesWarnButDirectReferenceNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageX); + projectB.Save(); + + // C -> X + projectC.AddPackageToAllFrameworks(packageX); + projectC.Save(); + + // A -> B + // A -> C + // A -> X + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.AddPackageToAllFrameworks(packageXWithNoWarn); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + GetSubstringCount(r.AllOutput, "NU1603", ignoreCase: false).Should().Be(2); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + public void GivenSinglePointOfReferenceNoWarnsVerifyNoWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageXWithNoWarn); + projectB.Save(); + + // C -> B + projectC.AddProjectToAllFrameworks(projectB); + projectC.Save(); + + // A -> B + // A -> C + // A -> X + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + [Fact] + // Tests ProjA -> ProjB[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> ProjC[PkgX NoWarn NU1603] -> PkgX[NU1603] + // -> ProjD -> ProjE -> ProjF -> PkgX[NU1603] + public void GivenOneLongPathWarnsVerifyWarning() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projectA = SimpleTestProjectContext.CreateNETCore( + "a", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectB = SimpleTestProjectContext.CreateNETCore( + "b", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectC = SimpleTestProjectContext.CreateNETCore( + "c", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectD = SimpleTestProjectContext.CreateNETCore( + "d", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectE = SimpleTestProjectContext.CreateNETCore( + "e", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + var projectF = SimpleTestProjectContext.CreateNETCore( + "f", + pathContext.SolutionRoot, + NuGetFramework.Parse("netcoreapp2.0")); + + + // Referenced but not created + var packageXWithNoWarn = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Referenced but not created + var packageX = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.0" + }; + + // Created in the source + var packageX11 = new SimpleTestPackageContext() + { + Id = "x", + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, packageX11); + + // B -> X + projectB.AddPackageToAllFrameworks(packageXWithNoWarn); + projectB.Save(); + + // C -> B + projectC.AddPackageToAllFrameworks(packageXWithNoWarn); + projectC.Save(); + + // F -> X + projectF.AddPackageToAllFrameworks(packageX); + projectF.Save(); + + // E -> F + projectE.AddProjectToAllFrameworks(projectF); + projectE.Save(); + + // D -> E + projectD.AddProjectToAllFrameworks(projectE); + projectD.Save(); + + // A -> B + // A -> C + // A -> D + projectA.AddProjectToAllFrameworks(projectB); + projectA.AddProjectToAllFrameworks(projectC); + projectA.AddProjectToAllFrameworks(projectD); + projectA.Save(); + + solution.Projects.Add(projectA); + solution.Projects.Add(projectB); + solution.Projects.Add(projectC); + solution.Projects.Add(projectD); + solution.Projects.Add(projectE); + solution.Projects.Add(projectF); + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().Contain("NU1603"); + } + } + + // Densely connected solutions containing 5, 10, 20 and 50 projects + + [Theory] + [InlineData(5)] + [InlineData(10)] + [InlineData(20)] + public void GivenDenseSolutionWithMultiplePathsVerifyNoWarn(int count) + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set up solution, project, and packages + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + + var projects = new List(); + var referencedPackages = new List(); + var createdPackages = new List(); + + for (var i = 0; i < count; i++) + { + // Referenced but not created + var packagewithNoWarn = new SimpleTestPackageContext() + { + Id = "package_" + i, + Version = "1.0.0", + NoWarn = "NU1603" + }; + + // Created in the source + var package = new SimpleTestPackageContext() + { + Id = "package_" + i, + Version = "1.0.1" + }; + + SimpleTestPackageUtility.CreatePackages(pathContext.PackageSource, package); + + referencedPackages.Add(packagewithNoWarn); + createdPackages.Add(package); + } + + for (var i = 0; i < count; i++) + { + var project = SimpleTestProjectContext.CreateNETCore( + "project_" + i, + pathContext.SolutionRoot, + NuGetFramework.Parse("net461")); + + projects.Add(project); + } + + for (var i = 1; i < projects.Count(); i++) + { + var project = projects[i]; + project.AddPackageToAllFrameworks(referencedPackages[i]); + } + + for (var i = 0; i < projects.Count() - 1; i++) + { + var projectA = projects[i]; + for (var j = i + 1; j < projects.Count(); j++) + { + var projectB = projects[j]; + projectA.AddProjectToAllFrameworks(projectB); + } + } + + foreach (var project in projects) + { + project.Save(); + solution.Projects.Add(project); + } + + solution.Create(pathContext.SolutionRoot); + + // Act + var r = Util.RestoreSolution(pathContext, expectedExitCode: 0); + + // Assert + r.Success.Should().BeTrue(); + r.AllOutput.Should().NotContain("NU1603"); + } + } + + private static int GetSubstringCount(string str, string substr, bool ignoreCase) + { + var splitChars = new char[] { '.', '?', '!', ' ', ';', ':', ',', '\r', '\n' }; + var words = str.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + var comparisonType = ignoreCase ? + StringComparison.OrdinalIgnoreCase : + StringComparison.Ordinal; + + return words + .Where(word => string.Equals(word, substr, comparisonType)) + .Count(); + } + } +} diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/CollectorLoggerTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/CollectorLoggerTests.cs index 6ddb026a6fc..f2370367f82 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/CollectorLoggerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/CollectorLoggerTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; @@ -112,10 +112,15 @@ public void CollectorLogger_DoesNotPassLogCallsToInnerLoggerByDefaultWithFilePat // Arrange var projectPath = @"kung/fu/fighting.csproj"; var innerLogger = new Mock(); - var collector = new RestoreCollectorLogger(innerLogger.Object, LogLevel.Debug, hideWarningsAndErrors: false) - { - ProjectPath = projectPath - }; + var collector = new RestoreCollectorLogger(innerLogger.Object, LogLevel.Debug, hideWarningsAndErrors: false); + collector.ApplyRestoreInputs( + new PackageSpec() + { + RestoreMetadata = new ProjectRestoreMetadata() + { + ProjectPath = projectPath + } + }); // Act collector.Log(LogLevel.Debug, "Debug"); @@ -368,10 +373,10 @@ public void CollectorLogger_LogsWarningsAsErrorsErrorsForProjectWideWarnAsErrorS var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -402,10 +407,10 @@ public void CollectorLogger_LogsWarningsAsErrorsForProjectAllWarningsAsErrors() var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -434,10 +439,10 @@ public void CollectorLogger_LogsWarningsAsErrorsForProjectAllWarningsAsErrorsAnd var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -468,10 +473,10 @@ public void CollectorLogger_DoesNotLogsWarningsForProjectWideNoWarnSet() var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -506,11 +511,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSet() var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act @@ -545,11 +549,10 @@ public void CollectorLogger_LogsWarningsForPackageSpecificNoWarnSetButWarningsWi var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act @@ -586,11 +589,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetWithMu var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act @@ -622,16 +624,15 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetWithLi var warnAsErrorSet = new HashSet { }; var allWarningsAsErrors = false; var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); - packageSpecificWarningProperties.AddRange(new List { NuGetLogCode.NU1500, NuGetLogCode.NU1601, NuGetLogCode.NU1605}, libraryId, targetFramework); + packageSpecificWarningProperties.AddRangeOfCodes(new List { NuGetLogCode.NU1500, NuGetLogCode.NU1601, NuGetLogCode.NU1605}, libraryId, targetFramework); var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act @@ -663,16 +664,15 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetWithLi var warnAsErrorSet = new HashSet { }; var allWarningsAsErrors = false; var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); - packageSpecificWarningProperties.AddRange(new List { NuGetLogCode.NU1500 }, libraryId, targetFramework); + packageSpecificWarningProperties.AddRangeOfCodes(new List { NuGetLogCode.NU1500 }, libraryId, targetFramework); var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act @@ -711,12 +711,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetForGlo var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework } - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework }) }; // Act @@ -753,12 +751,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetForGlo var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework, netcoreTargetFramework } - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework, netcoreTargetFramework }) }; // Act @@ -795,12 +791,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetWithMu var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework } - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework }) }; // Act @@ -841,12 +835,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetWithMu var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework, netcoreTargetFramework } - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework, netcoreTargetFramework }) }; // Act @@ -879,10 +871,10 @@ public void CollectorLogger_DoesNotLogsWarningsForProjectWideNoWarnSetAndAllWarn var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -911,10 +903,10 @@ public void CollectorLogger_DoesNotLogsWarningsForProjectWideNoWarnSetAndWarnAsE var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -943,10 +935,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetAndWar var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + null, + null) }; // Act @@ -983,11 +975,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificAndProjectWideN var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act @@ -1026,11 +1017,10 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetAndPro var innerLogger = new Mock(); var collector = new RestoreCollectorLogger(innerLogger.Object) { - WarningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - } + ProjectWarningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null) }; // Act diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/StandaloneProjectTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/StandaloneProjectTests.cs index f99ff3f9cf6..365c56f9723 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/StandaloneProjectTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/StandaloneProjectTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -47,6 +47,7 @@ public async Task StandaloneProject_BasicRestore() spec.RestoreMetadata.OutputPath = Path.Combine(pathContext.SolutionRoot, "x"); spec.RestoreMetadata.ProjectUniqueName = "x"; spec.RestoreMetadata.ProjectName = "x"; + spec.RestoreMetadata.ProjectPath = Path.Combine(pathContext.SolutionRoot, "x.csproj"); dgFile.AddProject(spec); dgFile.AddRestore("x"); diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/TransitiveNoWarnUtilsTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/TransitiveNoWarnUtilsTests.cs new file mode 100644 index 00000000000..c7311a2fd54 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/TransitiveNoWarnUtilsTests.cs @@ -0,0 +1,935 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Text; +using NuGet.Common; +using NuGet.Frameworks; +using NuGet.ProjectModel; +using Xunit; +using FluentAssertions; +using System.Diagnostics; + +namespace NuGet.Commands.Test +{ + public class TransitiveNoWarnUtilsTests + { + + // Tests for TransitiveNoWarnUtils.ExtractPathNoWarnProperties + [Fact] + public void ExtractPathNoWarnProperties_ReturnsEmptySetIfPathPropertiesAreNull() + { + // Arrange & Act + var extractedNoWarnSet = TransitiveNoWarnUtils.ExtractPathNoWarnProperties(null, "test_id"); + + // Assert + extractedNoWarnSet.Should().NotBeNull(); + extractedNoWarnSet.Should().BeEmpty(); + } + + [Fact] + public void ExtractPathNoWarnProperties_CorrectlyReadsProjectWideNoWarns() + { + // Arrange + var projectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1603 }; + var pathWarningProperties = new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarn, + null); + + // Act + var extractedNoWarnSet = TransitiveNoWarnUtils.ExtractPathNoWarnProperties( + pathWarningProperties, + "test_id"); + + // Assert + extractedNoWarnSet.Should().NotBeNullOrEmpty(); + extractedNoWarnSet.Should().BeEquivalentTo(projectWideNoWarn); + } + + [Fact] + public void ExtractPathNoWarnProperties_CorrectlyReadsPackageSpecificNoWarns() + { + // Arrange + var packageId = "test_package"; + var framework = NuGetFramework.Parse("net461"); + var expectedNoWarnSet = new HashSet { NuGetLogCode.NU1603, NuGetLogCode.NU1605 }; + + var pathWarningProperties = new TransitiveNoWarnUtils.NodeWarningProperties( + null, + new Dictionary> + { + {packageId, expectedNoWarnSet} + }); + + // Act + var extractedNoWarnSet = TransitiveNoWarnUtils.ExtractPathNoWarnProperties( + pathWarningProperties, + packageId); + + // Assert + extractedNoWarnSet.Should().NotBeNullOrEmpty(); + extractedNoWarnSet.Should().BeEquivalentTo(expectedNoWarnSet); + } + + + [Fact] + public void ExtractPathNoWarnProperties_CorrectlyReadsPackageSpecificAndProjectWideNoWarns() + { + // Arrange + var packageId = "test_package"; + var expectedNoWarnSet = new HashSet { NuGetLogCode.NU1601 , NuGetLogCode.NU1603, NuGetLogCode.NU1605, NuGetLogCode.NU1607 }; + var projectWideNoWarnSet = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1605 }; + var packageSpecificNoWarnSet = new HashSet { NuGetLogCode.NU1603, NuGetLogCode.NU1607 }; + var otherPackageSpecificNoWarnSet = new HashSet { NuGetLogCode.NU1603, NuGetLogCode.NU1701 }; + + var pathWarningProperties = new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarnSet, + new Dictionary> + { + {packageId, packageSpecificNoWarnSet}, + {"other_package", otherPackageSpecificNoWarnSet} + }); + + // Act + var extractedNoWarnSet = TransitiveNoWarnUtils.ExtractPathNoWarnProperties( + pathWarningProperties, + packageId); + + // Assert + extractedNoWarnSet.Should().NotBeNullOrEmpty(); + extractedNoWarnSet.Should().BeEquivalentTo(expectedNoWarnSet); + } + + + // Tests for TransitiveNoWarnUtils.TryMergeNullObjects + [Fact] + public void TryMergeNullObjects_ReturnsNullIfBothAreNull() + { + // Arrange + object mergedObject; + object first = null; + object second = null; + + // Act + var success = TransitiveNoWarnUtils.TryMergeNullObjects(first, second, out mergedObject); + + // Assert + success.Should().BeTrue(); + mergedObject.Should().BeNull(); + } + + [Fact] + public void TryMergeNullObjects_ReturnsFirstIfNotNull() + { + // Arrange + object mergedObject; + var first = new object(); + object second = null; + + // Act + var success = TransitiveNoWarnUtils.TryMergeNullObjects(first, second, out mergedObject); + + // Assert + success.Should().BeTrue(); + mergedObject.Should().Be(first); + } + + [Fact] + public void TryMergeNullObjects_ReturnsSecondIfNotNull() + { + // Arrange + object mergedObject; + object first = null; + var second = new object(); + + // Act + var success = TransitiveNoWarnUtils.TryMergeNullObjects(first, second, out mergedObject); + + // Assert + success.Should().BeTrue(); + mergedObject.Should().Be(second); + } + + [Fact] + public void TryMergeNullObjects_ReturnsFailureIfNoneNull() + { + // Arrange + object mergedObject; + var first = new object(); + var second = new object(); + + // Act + var success = TransitiveNoWarnUtils.TryMergeNullObjects(first, second, out mergedObject); + + // Assert + success.Should().BeFalse(); + mergedObject.Should().BeNull(); + } + + // Tests for TransitiveNoWarnUtils.MergePackageSpecificWarningProperties + [Fact] + public void MergePackageSpecificWarningProperties_ReturnsNullIfBothAreNull() + { + // Arrange + Dictionary> first = null; + Dictionary> second = null; + + // Act + var merged = TransitiveNoWarnUtils.MergePackageSpecificNoWarn(first, second); + + // Assert + merged.Should().BeNull(); + } + + [Fact] + public void MergePackageSpecificWarningProperties_ReturnsFirstIfNotNull() + { + // Arrange + var first = new Dictionary>(); + Dictionary> second = null; + + // Act + var merged = TransitiveNoWarnUtils.MergePackageSpecificNoWarn(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.Should().BeSameAs(first); + } + + [Fact] + public void MergePackageSpecificWarningProperties_ReturnsSecondIfNotNull() + { + // Arrange + Dictionary> first = null; + var second = new Dictionary>(); + + // Act + var merged = TransitiveNoWarnUtils.MergePackageSpecificNoWarn(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.Should().BeSameAs(second); + } + + [Fact] + public void MergePackageSpecificWarningProperties_MergesEmptyCollections() + { + // Arrange + var first = new Dictionary>(); + var second = new Dictionary>(); + + // Act + var merged = TransitiveNoWarnUtils.MergePackageSpecificNoWarn(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.Should().BeEmpty(); + } + + [Fact] + public void MergePackageSpecificWarningProperties_MergesNonEmptyCollections() + { + // Arrange + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + var net461 = NuGetFramework.Parse("net461"); + var netcoreapp = NuGetFramework.Parse("netcoreapp2.0"); + var expectedResult = new PackageSpecificWarningProperties(); + expectedResult.AddRangeOfCodes( + new List { NuGetLogCode.NU1601, NuGetLogCode.NU1605 }, + packageId1, + net461); + expectedResult.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + expectedResult.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId1, + new List { net461, netcoreapp }); + expectedResult.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + expectedResult.AddRangeOfFrameworks( + NuGetLogCode.NU1604, + packageId1, + new List { net461, netcoreapp }); + + + var first = new PackageSpecificWarningProperties(); + first.AddRangeOfCodes( + new List { NuGetLogCode.NU1601, NuGetLogCode.NU1605 }, + packageId1, + net461); + first.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + first.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId1, + new List { netcoreapp }); + + var second = new PackageSpecificWarningProperties(); + second.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + second.AddRangeOfFrameworks( + NuGetLogCode.NU1604, + packageId1, + new List { net461, netcoreapp }); + second.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId1, + new List { net461 }); + + + var expectedNoWarnForNet461 = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(expectedResult, net461); + var expectedNoWarnForNetcoreapp = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(expectedResult, netcoreapp); + + var firstNoWarnForNet461 = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(first, net461); + var firstNoWarnForNetcoreapp = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(first, netcoreapp); + + var secondNoWarnForNet461 = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(second, net461); + var secondNoWarnForNetcoreapp = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(second, netcoreapp); + + // Act + var mergedNoWarnForNet461 = TransitiveNoWarnUtils.MergePackageSpecificNoWarn(firstNoWarnForNet461, secondNoWarnForNet461); + var mergedNoWarnForNetcoreapp = TransitiveNoWarnUtils.MergePackageSpecificNoWarn(firstNoWarnForNetcoreapp, secondNoWarnForNetcoreapp); + + // Assert + mergedNoWarnForNet461.Should().NotBeNull(); + mergedNoWarnForNet461.ShouldBeEquivalentTo(expectedNoWarnForNet461); + + mergedNoWarnForNetcoreapp.Should().NotBeNull(); + mergedNoWarnForNetcoreapp.ShouldBeEquivalentTo(expectedNoWarnForNetcoreapp); + } + + // Tests for TransitiveNoWarnUtils.MergeProjectWideWarningProperties + [Fact] + public void MergeProjectWideWarningProperties_ReturnsNullIfBothAreNull() + { + // Arrange + HashSet first = null; + HashSet second = null; + + // Act + var merged = TransitiveNoWarnUtils.MergeCodes(first, second); + + // Assert + merged.Should().BeNull(); + } + + [Fact] + public void MergeProjectWideWarningProperties_ReturnsFirstIfNotNull() + { + // Arrange + var first = new HashSet(); + HashSet second = null; + + // Act + var merged = TransitiveNoWarnUtils.MergeCodes(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.Should().BeSameAs(first); + } + + [Fact] + public void MergeProjectWideWarningProperties_ReturnsSecondIfNotNull() + { + // Arrange + HashSet first = null; + var second = new HashSet(); + + // Act + var merged = TransitiveNoWarnUtils.MergeCodes(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.Should().BeSameAs(second); + } + + [Fact] + public void MergeProjectWideWarningProperties_MergesEmptyCollections() + { + // Arrange + var first = new HashSet(); + var second = new HashSet(); + + // Act + var merged = TransitiveNoWarnUtils.MergeCodes(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.Should().BeEmpty(); + } + + [Theory] + [InlineData("", "", "")] + [InlineData("NU1603, NU1605", "", "NU1603, NU1605")] + [InlineData("", "NU1603, NU1605", "NU1603, NU1605")] + [InlineData("NU1603, NU1605", "NU1603", "NU1603, NU1605")] + [InlineData("NU1603, NU1605", "NU1701", "NU1603, NU1605, NU1701")] + [InlineData("NU1603, NU1605", "NU1603, NU1607, NU1701", "NU1603, NU1605, NU1607, NU1701")] + [InlineData("NU1605, NU1603", "NU1607, NU1701, NU1603", "NU1603, NU1605, NU1607, NU1701")] + public void MergeProjectWideWarningProperties_MergesNonEmptyCollections( + string firstNoWarn, + string secondNoWarn, + string expectedNoWarn) + { + // Arrange + var first = new HashSet(MSBuildRestoreUtility.GetNuGetLogCodes(firstNoWarn)); + + var second = new HashSet(MSBuildRestoreUtility.GetNuGetLogCodes(secondNoWarn)); + + var expected = new HashSet(MSBuildRestoreUtility.GetNuGetLogCodes(expectedNoWarn)); + + // Act + var merged = TransitiveNoWarnUtils.MergeCodes(first, second); + + // Assert + merged.Should().NotBeNull(); + merged.ShouldBeEquivalentTo(expected); + } + + + // Tests for TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnPerFramework + [Fact] + public void ExtractPackageSpecificNoWarnForFrameworks_NullInput() + { + // Arrange + PackageSpecificWarningProperties input = null; + + // Act + var result = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnPerFramework(input); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void ExtractPackageSpecificNoWarnForFrameworks_InputWithNullProperties() + { + // Arrange + var input = new PackageSpecificWarningProperties(); + + // Act + var result = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnPerFramework(input); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void ExtractPackageSpecificNoWarnForFrameworks_InputWithProperties() + { + // Arrange + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + var net461 = NuGetFramework.Parse("net461"); + var netcoreapp = NuGetFramework.Parse("netcoreapp2.0"); + var input = new PackageSpecificWarningProperties(); + input.AddRangeOfCodes( + new List { NuGetLogCode.NU1601, NuGetLogCode.NU1605 }, + packageId1, + net461); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId1, + new List { net461, netcoreapp }); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1604, + packageId1, + new List { net461 }); + + var expected = new Dictionary>> + { + [net461] = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + [packageId1.ToUpper()] = new HashSet + { + NuGetLogCode.NU1601, + NuGetLogCode.NU1604, + NuGetLogCode.NU1605, + NuGetLogCode.NU1701, + }, + [packageId2.ToUpper()] = new HashSet + { + NuGetLogCode.NU1701 + } + }, + [netcoreapp] = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + [packageId1.ToLower()] = new HashSet + { + NuGetLogCode.NU1701, + }, + [packageId2.ToLower()] = new HashSet + { + NuGetLogCode.NU1701 + } + } + }; + + // Act + var result = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnPerFramework(input); + + // Assert + result.Should().NotBeNull(); + result.ShouldBeEquivalentTo(expected); + } + + // Tests for TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework + [Fact] + public void ExtractPackageSpecificNoWarnForFramework_NullInput() + { + // Arrange + PackageSpecificWarningProperties input = null; + NuGetFramework framework = null; + + // Act + var result = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(input, framework); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void ExtractPackageSpecificNoWarnForFramework_InputWithNullProperties() + { + // Arrange + var input = new PackageSpecificWarningProperties(); + NuGetFramework framework = null; + + // Act + var result = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(input, framework); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public void ExtractPackageSpecificNoWarnForFramework_InputWithProperties() + { + // Arrange + + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + var net461 = NuGetFramework.Parse("net461"); + var netcoreapp = NuGetFramework.Parse("netcoreapp2.0"); + var input = new PackageSpecificWarningProperties(); + input.AddRangeOfCodes( + new List { NuGetLogCode.NU1601, NuGetLogCode.NU1605 }, + packageId1, + net461); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId1, + new List { net461, netcoreapp }); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1701, + packageId2, + new List { net461, netcoreapp }); + input.AddRangeOfFrameworks( + NuGetLogCode.NU1604, + packageId1, + new List { net461 }); + + var expected = new Dictionary>> + { + [net461] = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + [packageId1.ToUpper()] = new HashSet + { + NuGetLogCode.NU1601, + NuGetLogCode.NU1604, + NuGetLogCode.NU1605, + NuGetLogCode.NU1701, + }, + [packageId2.ToUpper()] = new HashSet + { + NuGetLogCode.NU1701 + } + }, + [netcoreapp] = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + [packageId1.ToLower()] = new HashSet + { + NuGetLogCode.NU1701, + }, + [packageId2.ToLower()] = new HashSet + { + NuGetLogCode.NU1701 + } + } + }; + + // Act + var resultNet461 = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(input, net461); + var resultNetcoreapp = TransitiveNoWarnUtils.ExtractPackageSpecificNoWarnForFramework(input, netcoreapp); + + // Assert + resultNet461.Should().NotBeNull(); + resultNet461.ShouldBeEquivalentTo(expected[net461]); + resultNetcoreapp.Should().NotBeNull(); + resultNetcoreapp.ShouldBeEquivalentTo(expected[netcoreapp]); + } + + // Tests for TransitiveNoWarnUtils.DependencyNode equality + [Fact] + public void DependencyNodeEqualitySucceeds_NodesAreNull() + { + // Arrange + TransitiveNoWarnUtils.DependencyNode first = null; + TransitiveNoWarnUtils.DependencyNode second = null; + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(1); + } + + [Fact] + public void DependencyNodeEqualitySucceeds_OneNodeIsNull() + { + // Arrange + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties(null, null)); + + TransitiveNoWarnUtils.DependencyNode second = null; + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(2); + } + + [Fact] + public void DependencyNodeEqualitySucceeds_NodesAreSame() + { + // Arrange + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties(null, null)); + + // Act + var seen = new HashSet + { + first + }; + + // Assert + seen.Count.Should().Be(1); + } + + [Fact] + public void DependencyNodeEqualitySucceeds_NodesHaveSameInternalObjects() + { + // Arrange + var projectWideNoWarn = new HashSet(); + var packageSpecificNoWarn= new Dictionary>(); + + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarn, + packageSpecificNoWarn + )); + + var second = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarn, + packageSpecificNoWarn + )); + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(1); + } + + [Fact] + public void DependencyNodeEqualitySucceeds_NodesHaveEquivalentWarningProperties() + { + // Arrange + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + var net461 = NuGetFramework.Parse("net461"); + var netcoreapp = NuGetFramework.Parse("netcoreapp2.0"); + + + // Arrange + var firstProjectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1603 }; + var firstPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }}, + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var secondProjectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1603 }; + var secondPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }}, + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + firstProjectWideNoWarn, + firstPackageSpecificNoWarn + )); + + var second = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + secondProjectWideNoWarn, + secondPackageSpecificNoWarn + )); + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(1); + } + + [Fact] + public void DependencyNodeEqualityFails_NodesHaveDifferentMetaData() + { + // Arrange + var projectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1603 }; + var packageSpecificNoWarn = new Dictionary>(); + + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarn, + packageSpecificNoWarn + )); + + var second = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: false, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarn, + packageSpecificNoWarn + )); + + var third = new TransitiveNoWarnUtils.DependencyNode( + id: "test_other", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + projectWideNoWarn, + packageSpecificNoWarn + )); + + // Act + var seen = new HashSet + { + first, + second, + third + }; + + // Assert + seen.Count.Should().Be(3); + } + + [Theory] + [InlineData("NU1605, NU1701", "")] + [InlineData("", "NU1605, NU1701")] + [InlineData("NU1605, NU1701", "NU1604")] + [InlineData("NU1604, NU1701", "NU1604")] + [InlineData("NU1701", "NU1604, NU1701")] + public void DependencyNodeEqualityFails_NodesHaveDifferentProjectWideWarningProperties( + string firstNoWarn, + string secondNoWarn) + { + // Arrange + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + + // Arrange + var firstProjectWideNoWarn = new HashSet(MSBuildRestoreUtility.GetNuGetLogCodes(firstNoWarn)); + var firstPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }}, + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var secondProjectWideNoWarn = new HashSet(MSBuildRestoreUtility.GetNuGetLogCodes(secondNoWarn)); + var secondPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }}, + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + firstProjectWideNoWarn, + firstPackageSpecificNoWarn + )); + + var second = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + secondProjectWideNoWarn, + secondPackageSpecificNoWarn + )); + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(2); + } + + [Fact] + public void DependencyNodeEqualityFails_NodesHaveDifferentNoWarnCodesInPackageSpecificNoWarn() + { + // Arrange + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + + // Arrange + var firstProjectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }; + var firstPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }}, + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var secondProjectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }; + var secondPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }}, + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + firstProjectWideNoWarn, + firstPackageSpecificNoWarn + )); + + var second = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + secondProjectWideNoWarn, + secondPackageSpecificNoWarn + )); + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(2); + } + + [Fact] + public void DependencyNodeEqualityFails_NodesHaveDifferentPackageIdsInPackageSpecificNoWarn() + { + // Arrange + var packageId1 = "test_id1"; + var packageId2 = "test_id2"; + + // Arrange + var firstProjectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }; + var firstPackageSpecificNoWarn = new Dictionary> + { + {packageId1, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var secondProjectWideNoWarn = new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1606 }; + var secondPackageSpecificNoWarn = new Dictionary> + { + {packageId2, new HashSet { NuGetLogCode.NU1601, NuGetLogCode.NU1701 }} + }; + + var first = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + firstProjectWideNoWarn, + firstPackageSpecificNoWarn + )); + + var second = new TransitiveNoWarnUtils.DependencyNode( + id: "test", + isProject: true, + nodeWarningProperties: new TransitiveNoWarnUtils.NodeWarningProperties( + secondProjectWideNoWarn, + secondPackageSpecificNoWarn + )); + + // Act + var seen = new HashSet + { + first, + second + }; + + // Assert + seen.Count.Should().Be(2); + } + } +} diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/WarningPropertiesCollectionTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/WarningPropertiesCollectionTests.cs index 19f6a35d1da..ad788eeb839 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/WarningPropertiesCollectionTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/WarningPropertiesCollectionTests.cs @@ -21,10 +21,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithNoWarn() var noWarnSet = new HashSet { NuGetLogCode.NU1500 }; var warnAsErrorSet = new HashSet { }; var allWarningsAsErrors = false; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var suppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning"); var nonSuppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -42,10 +39,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithWarnAsError() var noWarnSet = new HashSet { }; var warnAsErrorSet = new HashSet { NuGetLogCode.NU1500 }; var allWarningsAsErrors = false; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var upgradedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning"); var nonSuppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -64,10 +58,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithWarnAsErrorAndUndef var noWarnSet = new HashSet { }; var warnAsErrorSet = new HashSet { NuGetLogCode.Undefined }; var allWarningsAsErrors = false; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var nonSuppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.Undefined, "Warning"); var nonSuppressedMessage2 = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -87,10 +78,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithAllWarningsAsErrors var noWarnSet = new HashSet { }; var warnAsErrorSet = new HashSet { }; var allWarningsAsErrors = true; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var upgradedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning"); var upgradedMessage2 = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -109,10 +97,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithAllWarningsAsErrors var noWarnSet = new HashSet { }; var warnAsErrorSet = new HashSet { }; var allWarningsAsErrors = true; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var upgradedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.Undefined, "Warning"); var upgradedMessage2 = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -132,10 +117,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithNoWarnAndWarnAsErro var noWarnSet = new HashSet { NuGetLogCode.NU1500 }; var warnAsErrorSet = new HashSet { NuGetLogCode.NU1500 }; var allWarningsAsErrors = false; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var suppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning"); var nonSuppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -153,10 +135,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithNoWarnAndWarnAsErro var noWarnSet = new HashSet { NuGetLogCode.NU1500 }; var warnAsErrorSet = new HashSet { NuGetLogCode.NU1500 }; var allWarningsAsErrors = true; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var suppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning"); var nonSuppressedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -174,10 +153,7 @@ public void WarningPropertiesCollection_ProjectPropertiesWithWarnAsErrorAndAllWa var noWarnSet = new HashSet { }; var warnAsErrorSet = new HashSet { NuGetLogCode.NU1500 }; var allWarningsAsErrors = true; - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors) - }; + var warningPropertiesCollection = new WarningPropertiesCollection(new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), null, null); var upgradedMessage = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning"); var upgradedMessage2 = new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1601, "Warning"); @@ -204,10 +180,7 @@ public void WarningPropertiesCollection_PackagePropertiesWithFrameworkAndWarning var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection(null, packageSpecificWarningProperties, null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); var nonSuppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1601, "Warning", libraryId, frameworkString); @@ -233,10 +206,7 @@ public void WarningPropertiesCollection_PackagePropertiesWithATFFrameworkAndWarn var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection(null, packageSpecificWarningProperties, null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); @@ -259,10 +229,7 @@ public void WarningPropertiesCollection_PackagePropertiesWithPTFFrameworkAndWarn var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection(null, packageSpecificWarningProperties, null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); @@ -285,11 +252,7 @@ public void WarningPropertiesCollection_PackagePropertiesWithoutFrameworkAndWarn var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection(null, packageSpecificWarningProperties, new List { targetFramework }); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); @@ -312,10 +275,7 @@ public void WarningPropertiesCollection_PackagePropertiesWithoutFrameworkAndWarn var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection(null, packageSpecificWarningProperties, null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, "net45"); @@ -342,11 +302,10 @@ public void WarningPropertiesCollection_PackagePropertiesWithNoWarnAndProjectPro var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); @@ -372,11 +331,10 @@ public void WarningPropertiesCollection_PackagePropertiesAndProjectPropertiesWit var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); @@ -403,15 +361,10 @@ public void WarningPropertiesCollection_PackagePropertiesWithNoWarnAndProjectPro var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List - { - targetFramework - } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework }); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); var suppressedMessage2 = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); @@ -443,15 +396,10 @@ public void WarningPropertiesCollection_PackagePropertiesWithNoWarnAndProjectPro var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List - { - targetFramework - } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework }); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); var suppressedMessage2 = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); @@ -485,12 +433,10 @@ public void WarningPropertiesCollection_PackagePropertiesWithNoWarnAndProjectPro var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework }); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString); var suppressedMessage2 = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); @@ -527,12 +473,10 @@ public void WarningPropertiesCollection_MessageWithNoTargetGraphAndDependencyWit var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, firstTargetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { firstTargetFramework, secondTargetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { firstTargetFramework, secondTargetFramework }); var nonSuppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, firstFrameworkString); @@ -567,12 +511,10 @@ public void WarningPropertiesCollection_MessageWithNoTargetGraphAndDependencyWit packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, firstTargetFramework); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, secondTargetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { firstTargetFramework, secondTargetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { firstTargetFramework, secondTargetFramework }); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId); @@ -603,12 +545,10 @@ public void WarningPropertiesCollection_MessageWithTargetGraphAndDependencyWithN var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, firstTargetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { firstTargetFramework, secondTargetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { firstTargetFramework, secondTargetFramework }); var nonSuppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, new string[] { firstFrameworkString, secondFrameworkString }); @@ -639,12 +579,10 @@ public void WarningPropertiesCollection_MessageWithTargetGraphAndDependencyWithN packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, firstTargetFramework); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, secondTargetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { firstTargetFramework, secondTargetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { firstTargetFramework, secondTargetFramework }); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, new string[] { firstFrameworkString, secondFrameworkString }); @@ -672,12 +610,10 @@ public void WarningPropertiesCollection_MessageWithTargetGraphAndDependencyWithN var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties, - ProjectFrameworks = new List { targetFramework } - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + new List { targetFramework}); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, new string[] { frameworkString }); @@ -708,11 +644,10 @@ public void WarningPropertiesCollection_MessageWithTargetGraphAndDependencyWithN var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, firstTargetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null); var nonSuppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, new string[] { firstFrameworkString, secondFrameworkString }); @@ -743,11 +678,10 @@ public void WarningPropertiesCollection_MessageWithTargetGraphAndDependencyWithN packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, firstTargetFramework); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, secondTargetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, new string[] { firstFrameworkString, secondFrameworkString }); @@ -775,11 +709,10 @@ public void WarningPropertiesCollection_MessageWithTargetGraphAndDependencyWithN var packageSpecificWarningProperties = new PackageSpecificWarningProperties(); packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework); - var warningPropertiesCollection = new WarningPropertiesCollection() - { - ProjectWideWarningProperties = new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), - PackageSpecificWarningProperties = packageSpecificWarningProperties - }; + var warningPropertiesCollection = new WarningPropertiesCollection( + new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors), + packageSpecificWarningProperties, + null); var suppressedMessage = RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, new string[] { frameworkString }); diff --git a/test/NuGet.Core.Tests/NuGet.Common.Test/PackageSpecificWanringPropertiesTests.cs b/test/NuGet.Core.Tests/NuGet.Common.Test/PackageSpecificWanringPropertiesTests.cs index cf001b9cd24..58bf9074980 100644 --- a/test/NuGet.Core.Tests/NuGet.Common.Test/PackageSpecificWanringPropertiesTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Common.Test/PackageSpecificWanringPropertiesTests.cs @@ -54,7 +54,7 @@ public void PackageSpecificWarningProperties_AddsRangeValueWithGlobalTFM() var libraryId = "test_libraryId"; var targetFramework = NuGetFramework.Parse("net45"); var properties = new PackageSpecificWarningProperties(); - properties.AddRange(codes, libraryId, targetFramework); + properties.AddRangeOfCodes(codes, libraryId, targetFramework); // Assert foreach (var code in codes)