From 3888ff66d67876f2cfc81c413237231f32e9e5a5 Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Fri, 7 Feb 2020 16:21:54 -0500 Subject: [PATCH 1/9] refactored updater tool for unit testing --- src/NuGet.Updater.Tool/ConsoleArgsParser.cs | 79 +++++++++++++++++++++ src/NuGet.Updater.Tool/Program.cs | 60 ++-------------- 2 files changed, 84 insertions(+), 55 deletions(-) create mode 100644 src/NuGet.Updater.Tool/ConsoleArgsParser.cs diff --git a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs b/src/NuGet.Updater.Tool/ConsoleArgsParser.cs new file mode 100644 index 0000000..e978c85 --- /dev/null +++ b/src/NuGet.Updater.Tool/ConsoleArgsParser.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Mono.Options; +using Newtonsoft.Json; +using NuGet.Packaging; +using NuGet.Shared.Entities; +using NuGet.Updater.Entities; +using NuGet.Versioning; + +namespace NuGet.Updater.Tool +{ + public static class ConsoleArgsParser + { + public static (ConsoleArgsContext Context, OptionSet Options) Parse(string[] args) + { + var isParameterSet = false; + var context = new ConsoleArgsContext + { + Parameters = new UpdaterParameters { UpdateTarget = FileType.All }, + }; + + var options = new OptionSet + { + { "help|h", "Displays this help screen", s => context.IsHelp = true }, + { "solution=|s=", "The {path} to the solution or folder to update; defaults to the current folder", s => Set(p => p.SolutionRoot = s) }, + { "feed=|f=", "A NuGet feed to use for the update; a private feed can be specified with the format {url|accessToken}; can be specified multiple times", s => Set(p => p.Feeds.Add(PackageFeed.FromString(s)))}, + { "version=|versions=|v=", "The target {version} to use; latest stable is always considered; can be specified multiple times", s => Set(p => p.TargetVersions.Add(s))}, + { "ignorePackages=|ignore=|i=", "A specific {package} to ignore; can be specified multiple times", s => Set(p => p.PackagesToIgnore.Add(s)) }, + { "updatePackages=|update=|u=", "A specific {package} to update; not specifying this will update all packages found; can be specified multiple times", s => Set(p => p.PackagesToUpdate.Add(s)) }, + { "packageAuthor=|a=", "The {author} of the packages to update; used for public packages only", s => Set(p => p.PackageAuthor = s)}, + { "outputFile=|of=", "The {path} to a markdown file where the update summary will be written", s => context.SummaryFile = s }, + { "allowDowngrade|d", "Whether package downgrade is allowed", s => Set(p => p.IsDowngradeAllowed = true)}, + { "useNuGetorg|n", "Whether to use packages from NuGet.org", _ => Set(p => p.Feeds.Add(PackageFeed.NuGetOrg)) }, + { "silent", "Suppress all output from NuGet Updater", _ => context.IsSilent = true }, + { "strict", "Whether to use versions with only the specified version tag (ie. dev, but not dev.test)", _ => Set(p => p.Strict = true) }, + { "dryrun", "Runs the updater but doesn't write the updates to files.", _ => Set(p => p.IsDryRun = true) }, + { "result|r=", "The path to the file where the update result should be saved.", s => context.ResultFile = s }, + { "versionOverrides=", "The path to a JSON file to force specifc versions to be used; format should be the same as the result file", s => Set(p => p.VersionOverrides.AddRange(LoadManualOperations(s))) }, + }; + + options.Parse(args); + context.IsHelp |= !isParameterSet; + + return (context, options); + + void Set(Action set) + { + set(context.Parameters); + isParameterSet = true; + } + } + + private static Dictionary LoadManualOperations(string inputFilePath) + { + using(var fileReader = File.OpenText(inputFilePath)) + using(var jsonReader = new JsonTextReader(fileReader)) + { + var result = JsonSerializer.CreateDefault().Deserialize>(jsonReader); + + return result.ToDictionary(r => r.PackageId, r => new NuGetVersion(r.UpdatedVersion)); + } + } + + public class ConsoleArgsContext + { + public bool IsHelp { get; set; } + + public bool IsSilent { get; set; } + + public string SummaryFile { get; set; } + + public string ResultFile { get; set; } + + public UpdaterParameters Parameters { get; set; } + } + } +} diff --git a/src/NuGet.Updater.Tool/Program.cs b/src/NuGet.Updater.Tool/Program.cs index 33e96c4..27310ca 100644 --- a/src/NuGet.Updater.Tool/Program.cs +++ b/src/NuGet.Updater.Tool/Program.cs @@ -17,47 +17,14 @@ namespace NuGet.Updater.Tool { public class Program { - private static bool _isParameterSet = default; - private static UpdaterParameters _parameters = default; - public static async Task Main(string[] args) { try { - var isHelp = false; - var isSilent = false; - string summaryFile = default; - string resultFile = default; - - var options = new OptionSet - { - { "help|h", "Displays this help screen", s => isHelp = true }, - { "solution=|s=", "The {path} to the solution or folder to update; defaults to the current folder", s => Set(p => p.SolutionRoot = s) }, - { "feed=|f=", "A NuGet feed to use for the update; a private feed can be specified with the format {url|accessToken}; can be specified multiple times", s => Set(p => p.Feeds.Add(PackageFeed.FromString(s)))}, - { "version=|versions=|v=", "The target {version} to use; latest stable is always considered; can be specified multiple times", s => Set(p => p.TargetVersions.Add(s))}, - { "ignorePackages=|ignore=|i=", "A specific {package} to ignore; can be specified multiple times", s => Set(p => p.PackagesToIgnore.Add(s)) }, - { "updatePackages=|update=|u=", "A specific {package} to update; not specifying this will update all packages found; can be specified multiple times", s => Set(p => p.PackagesToUpdate.Add(s)) }, - { "packageAuthor=|a=", "The {author} of the packages to update; used for public packages only", s => Set(p => p.PackageAuthor = s)}, - { "outputFile=|of=", "The {path} to a markdown file where the update summary will be written", s => summaryFile = s }, - { "allowDowngrade|d", "Whether package downgrade is allowed", s => Set(p => p.IsDowngradeAllowed = true)}, - { "useNuGetorg|n", "Whether to use packages from NuGet.org", _ => Set(p => p.Feeds.Add(PackageFeed.NuGetOrg)) }, - { "silent", "Suppress all output from NuGet Updater", _ => isSilent = true }, - { "strict", "Whether to use versions with only the specified version tag (ie. dev, but not dev.test)", _ => Set(p => p.Strict = true) }, - { "dryrun", "Runs the updater but doesn't write the updates to files.", _ => Set(p => p.IsDryRun = true) }, - { "result|r=", "The path to the file where the update result should be saved.", s => resultFile = s }, - { "versionOverrides=", "The path to a JSON file to force specifc versions to be used; format should be the same as the result file", s => Set(p => p.VersionOverrides.AddRange(LoadManualOperations(s))) }, - }; - - _isParameterSet = false; - _parameters = new UpdaterParameters - { - SolutionRoot = Environment.CurrentDirectory, - UpdateTarget = FileType.All, - }; - - options.Parse(args); + var (context, options) = ConsoleArgsParser.Parse(args); + context.Parameters.SolutionRoot ??= Environment.CurrentDirectory; - if(isHelp || !_isParameterSet) + if(context.IsHelp) { Console.WriteLine("NuGet Updater is a tool allowing the automatic update of the NuGet packages found in a solution"); Console.WriteLine(); @@ -65,11 +32,11 @@ public static async Task Main(string[] args) } else { - var updater = new NuGetUpdater(_parameters, isSilent ? null : Console.Out, GetSummaryWriter(summaryFile)); + var updater = new NuGetUpdater(context.Parameters, context.IsSilent ? null : Console.Out, GetSummaryWriter(context.SummaryFile)); var result = await updater.UpdatePackages(CancellationToken.None); - Save(result, resultFile); + Save(result, context.ResultFile); } } catch(Exception ex) @@ -78,12 +45,6 @@ public static async Task Main(string[] args) } } - private static void Set(Action set) - { - set(_parameters); - _isParameterSet = true; - } - private static TextWriter GetSummaryWriter(string summaryFile) => summaryFile == null ? null : new SimpleTextWriter(line => FileHelper.LogToFile(summaryFile, line)); private static void Save(IEnumerable result, string path) @@ -100,16 +61,5 @@ private static void Save(IEnumerable result, string path) serializer.Serialize(writer, result); } } - - private static Dictionary LoadManualOperations(string inputFilePath) - { - using(var fileReader = File.OpenText(inputFilePath)) - using(var jsonReader = new JsonTextReader(fileReader)) - { - var result = JsonSerializer.CreateDefault().Deserialize>(jsonReader); - - return result.ToDictionary(r => r.PackageId, r => new NuGetVersion(r.UpdatedVersion)); - } - } } } From 213db472a59b72f7d00b8470b6fd0b6141b3ba0a Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Fri, 7 Feb 2020 16:44:15 -0500 Subject: [PATCH 2/9] Added ConsoleArgsParserTests --- .../ConsoleArgsParserTests.cs | 31 +++++++++++++++++++ .../NuGet.Updater.Tests.csproj | 5 +-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs new file mode 100644 index 0000000..643186a --- /dev/null +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NuGet.Updater.Tool; + +namespace NuGet.Updater.Tests +{ + [TestClass] + public class ConsoleArgsParserTests + { + [TestMethod] + public void Given_HelpArgument_ContextIsHelp() + { + var arguments = new[] { "-help" }; + var (context, _) = ConsoleArgsParser.Parse(arguments); + + Assert.IsTrue(context.IsHelp); + } + + [TestMethod] + public void Given_InvalidArgument_ContextIsError() + { + var arguments = new[] { "--absolutelyWrong" }; + var (context, _) = ConsoleArgsParser.Parse(arguments); + + //throw new NotImplementedException(); + Assert.IsTrue(context.IsHelp); + } + } +} diff --git a/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj b/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj index cdbb4f9..6d03a73 100644 --- a/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj +++ b/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj @@ -1,19 +1,20 @@  - netcoreapp2.1 + netcoreapp3.0 false - + + From a4bfed6de013f0ffd1f6635e23c04a37c1c2bc81 Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Fri, 7 Feb 2020 17:58:48 -0500 Subject: [PATCH 3/9] added handling for unrecognized argument --- .../ConsoleArgsParserTests.cs | 11 ++- src/NuGet.Updater.Tool/ConsoleArgsParser.cs | 92 +++++++++++++------ src/NuGet.Updater.Tool/Program.cs | 13 ++- 3 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs index 643186a..1d182f5 100644 --- a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -13,19 +13,20 @@ public class ConsoleArgsParserTests public void Given_HelpArgument_ContextIsHelp() { var arguments = new[] { "-help" }; - var (context, _) = ConsoleArgsParser.Parse(arguments); + var context = ConsoleArgsParser.Parse(arguments); Assert.IsTrue(context.IsHelp); } [TestMethod] - public void Given_InvalidArgument_ContextIsError() + public void Given_UnrecognizedArgument_ContextHasError() { var arguments = new[] { "--absolutelyWrong" }; - var (context, _) = ConsoleArgsParser.Parse(arguments); + var context = ConsoleArgsParser.Parse(arguments); - //throw new NotImplementedException(); - Assert.IsTrue(context.IsHelp); + Assert.IsTrue(context.HasError); + Assert.AreEqual(context.Errors[0].Argument, arguments[0]); + Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.UnrecognizedArgument); } } } diff --git a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs b/src/NuGet.Updater.Tool/ConsoleArgsParser.cs index e978c85..8174b0b 100644 --- a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs +++ b/src/NuGet.Updater.Tool/ConsoleArgsParser.cs @@ -4,52 +4,56 @@ using System.Linq; using Mono.Options; using Newtonsoft.Json; -using NuGet.Packaging; using NuGet.Shared.Entities; using NuGet.Updater.Entities; using NuGet.Versioning; +using Uno.Extensions; namespace NuGet.Updater.Tool { public static class ConsoleArgsParser { - public static (ConsoleArgsContext Context, OptionSet Options) Parse(string[] args) - { - var isParameterSet = false; - var context = new ConsoleArgsContext - { - Parameters = new UpdaterParameters { UpdateTarget = FileType.All }, - }; + public static OptionSet GetOptions() => CreateOptionsFor(default); - var options = new OptionSet + private static OptionSet CreateOptionsFor(ConsoleArgsContext context = null) + { + return new OptionSet { { "help|h", "Displays this help screen", s => context.IsHelp = true }, - { "solution=|s=", "The {path} to the solution or folder to update; defaults to the current folder", s => Set(p => p.SolutionRoot = s) }, - { "feed=|f=", "A NuGet feed to use for the update; a private feed can be specified with the format {url|accessToken}; can be specified multiple times", s => Set(p => p.Feeds.Add(PackageFeed.FromString(s)))}, - { "version=|versions=|v=", "The target {version} to use; latest stable is always considered; can be specified multiple times", s => Set(p => p.TargetVersions.Add(s))}, - { "ignorePackages=|ignore=|i=", "A specific {package} to ignore; can be specified multiple times", s => Set(p => p.PackagesToIgnore.Add(s)) }, - { "updatePackages=|update=|u=", "A specific {package} to update; not specifying this will update all packages found; can be specified multiple times", s => Set(p => p.PackagesToUpdate.Add(s)) }, - { "packageAuthor=|a=", "The {author} of the packages to update; used for public packages only", s => Set(p => p.PackageAuthor = s)}, + { "solution=|s=", "The {path} to the solution or folder to update; defaults to the current folder", s => Set(p => p.Parameters.SolutionRoot = s) }, + { "feed=|f=", "A NuGet feed to use for the update; a private feed can be specified with the format {url|accessToken}; can be specified multiple times", s => Set(p => p.Parameters.Feeds.Add(PackageFeed.FromString(s)))}, + { "version=|versions=|v=", "The target {version} to use; latest stable is always considered; can be specified multiple times", s => Set(p => p.Parameters.TargetVersions.Add(s))}, + { "ignorePackages=|ignore=|i=", "A specific {package} to ignore; can be specified multiple times", s => Set(p => p.Parameters.PackagesToIgnore.Add(s)) }, + { "updatePackages=|update=|u=", "A specific {package} to update; not specifying this will update all packages found; can be specified multiple times", s => Set(p => p.Parameters.PackagesToUpdate.Add(s)) }, + { "packageAuthor=|a=", "The {author} of the packages to update; used for public packages only", s => Set(p => p.Parameters.PackageAuthor = s)}, { "outputFile=|of=", "The {path} to a markdown file where the update summary will be written", s => context.SummaryFile = s }, - { "allowDowngrade|d", "Whether package downgrade is allowed", s => Set(p => p.IsDowngradeAllowed = true)}, - { "useNuGetorg|n", "Whether to use packages from NuGet.org", _ => Set(p => p.Feeds.Add(PackageFeed.NuGetOrg)) }, + { "allowDowngrade|d", "Whether package downgrade is allowed", s => Set(p => p.Parameters.IsDowngradeAllowed = true)}, + { "useNuGetorg|n", "Whether to use packages from NuGet.org", _ => Set(p => p.Parameters.Feeds.Add(PackageFeed.NuGetOrg)) }, { "silent", "Suppress all output from NuGet Updater", _ => context.IsSilent = true }, - { "strict", "Whether to use versions with only the specified version tag (ie. dev, but not dev.test)", _ => Set(p => p.Strict = true) }, - { "dryrun", "Runs the updater but doesn't write the updates to files.", _ => Set(p => p.IsDryRun = true) }, + { "strict", "Whether to use versions with only the specified version tag (ie. dev, but not dev.test)", _ => Set(p => p.Parameters.Strict = true) }, + { "dryrun", "Runs the updater but doesn't write the updates to files.", _ => Set(p => p.Parameters.IsDryRun = true) }, { "result|r=", "The path to the file where the update result should be saved.", s => context.ResultFile = s }, - { "versionOverrides=", "The path to a JSON file to force specifc versions to be used; format should be the same as the result file", s => Set(p => p.VersionOverrides.AddRange(LoadManualOperations(s))) }, + { "versionOverrides=", "The path to a JSON file to force specifc versions to be used; format should be the same as the result file", s => Set(p => p.Parameters.VersionOverrides.AddRange(LoadManualOperations(s))) }, }; - options.Parse(args); - context.IsHelp |= !isParameterSet; - - return (context, options); + void Set(Action setter) => context?.Apply(setter); + } - void Set(Action set) + public static ConsoleArgsContext Parse(string[] args) + { + if (args.Empty()) { - set(context.Parameters); - isParameterSet = true; + return new ConsoleArgsContext { IsHelp = true }; } + + var context = new ConsoleArgsContext + { + Parameters = new UpdaterParameters { UpdateTarget = FileType.All }, + }; + var unparsed = CreateOptionsFor(context).Parse(args); + context.Errors.AddRange(unparsed.Select(ConsoleArgError.UnrecognizedArgument)); + + return context; } private static Dictionary LoadManualOperations(string inputFilePath) @@ -65,6 +69,10 @@ private static Dictionary LoadManualOperations(string inpu public class ConsoleArgsContext { + public bool HasError => Errors.Any(); + + public IList Errors { get; } = new List(); + public bool IsHelp { get; set; } public bool IsSilent { get; set; } @@ -75,5 +83,35 @@ public class ConsoleArgsContext public UpdaterParameters Parameters { get; set; } } + + public class ConsoleArgError + { + public ErrorType Type { get; set; } + + public string Argument { get; set; } + + public Exception Exception { get; set; } + + public ConsoleArgError(string argument, ErrorType type, Exception e = null) + { + Argument = argument; + Type = type; + Exception = e; + } + + public string Message => Type switch + { + ErrorType.UnrecognizedArgument => "unrecognized argument: " + Argument, + _ => $"{Type}: " + Argument, + }; + + internal static ConsoleArgError UnrecognizedArgument(string argument) => new ConsoleArgError(argument, ErrorType.UnrecognizedArgument); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Self-Explantory")] + public enum ErrorType + { + UnrecognizedArgument, + } + } } } diff --git a/src/NuGet.Updater.Tool/Program.cs b/src/NuGet.Updater.Tool/Program.cs index 27310ca..b6d8a0d 100644 --- a/src/NuGet.Updater.Tool/Program.cs +++ b/src/NuGet.Updater.Tool/Program.cs @@ -21,17 +21,22 @@ public static async Task Main(string[] args) { try { - var (context, options) = ConsoleArgsParser.Parse(args); - context.Parameters.SolutionRoot ??= Environment.CurrentDirectory; + var context = ConsoleArgsParser.Parse(args); - if(context.IsHelp) + if (context.HasError) + { + Console.Error.WriteLine(context.Errors.FirstOrDefault().Message); + Environment.Exit(-1); + } + else if (context.IsHelp) { Console.WriteLine("NuGet Updater is a tool allowing the automatic update of the NuGet packages found in a solution"); Console.WriteLine(); - options.WriteOptionDescriptions(Console.Out); + ConsoleArgsParser.GetOptions().WriteOptionDescriptions(Console.Out); } else { + context.Parameters.SolutionRoot ??= Environment.CurrentDirectory; var updater = new NuGetUpdater(context.Parameters, context.IsSilent ? null : Console.Out, GetSummaryWriter(context.SummaryFile)); var result = await updater.UpdatePackages(CancellationToken.None); From 0c92379c32ac823ccf5d3946c8e23a56920ea53e Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Mon, 10 Feb 2020 17:24:06 -0500 Subject: [PATCH 4/9] added exception handling for argument parsing/assignment --- .../ConsoleArgsParserTests.cs | 28 ++++++- src/NuGet.Updater.Tool/ConsoleArgsParser.cs | 82 ++++++++++++++----- 2 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs index 1d182f5..12a720f 100644 --- a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.IO; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using NuGet.Updater.Tool; @@ -28,5 +29,30 @@ public void Given_UnrecognizedArgument_ContextHasError() Assert.AreEqual(context.Errors[0].Argument, arguments[0]); Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.UnrecognizedArgument); } + + [TestMethod] + [Ignore("fixme: Mono.Options recognizes `- asd` because it is parsed as parameter `-a` with a value of `sd`")] + public void Given_UnrecognizedArgument_ContextHasError2() + { + var arguments = new[] { "-asd" }; + var context = ConsoleArgsParser.Parse(arguments); + + Assert.IsTrue(context.HasError); + Assert.AreEqual(context.Errors[0].Argument, arguments[0]); + Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.UnrecognizedArgument); + } + + [TestMethod] + public void Given_InvalidArgumentParameter_ContextHasError() + { + const string MissingFile = @"c:\not\existing\file.mia"; + var arguments = new[] { $"--versionOverrides={MissingFile}" }; + var context = ConsoleArgsParser.Parse(arguments); + + Assert.IsTrue(context.HasError); + Assert.AreEqual(context.Errors[0].Argument, MissingFile); + Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.ValueParsingError); + Assert.IsInstanceOfType(context.Errors[0].Exception, typeof(DirectoryNotFoundException)); + } } } diff --git a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs b/src/NuGet.Updater.Tool/ConsoleArgsParser.cs index 8174b0b..40c352d 100644 --- a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs +++ b/src/NuGet.Updater.Tool/ConsoleArgsParser.cs @@ -19,24 +19,65 @@ private static OptionSet CreateOptionsFor(ConsoleArgsContext context = null) { return new OptionSet { - { "help|h", "Displays this help screen", s => context.IsHelp = true }, - { "solution=|s=", "The {path} to the solution or folder to update; defaults to the current folder", s => Set(p => p.Parameters.SolutionRoot = s) }, - { "feed=|f=", "A NuGet feed to use for the update; a private feed can be specified with the format {url|accessToken}; can be specified multiple times", s => Set(p => p.Parameters.Feeds.Add(PackageFeed.FromString(s)))}, - { "version=|versions=|v=", "The target {version} to use; latest stable is always considered; can be specified multiple times", s => Set(p => p.Parameters.TargetVersions.Add(s))}, - { "ignorePackages=|ignore=|i=", "A specific {package} to ignore; can be specified multiple times", s => Set(p => p.Parameters.PackagesToIgnore.Add(s)) }, - { "updatePackages=|update=|u=", "A specific {package} to update; not specifying this will update all packages found; can be specified multiple times", s => Set(p => p.Parameters.PackagesToUpdate.Add(s)) }, - { "packageAuthor=|a=", "The {author} of the packages to update; used for public packages only", s => Set(p => p.Parameters.PackageAuthor = s)}, - { "outputFile=|of=", "The {path} to a markdown file where the update summary will be written", s => context.SummaryFile = s }, - { "allowDowngrade|d", "Whether package downgrade is allowed", s => Set(p => p.Parameters.IsDowngradeAllowed = true)}, - { "useNuGetorg|n", "Whether to use packages from NuGet.org", _ => Set(p => p.Parameters.Feeds.Add(PackageFeed.NuGetOrg)) }, - { "silent", "Suppress all output from NuGet Updater", _ => context.IsSilent = true }, - { "strict", "Whether to use versions with only the specified version tag (ie. dev, but not dev.test)", _ => Set(p => p.Parameters.Strict = true) }, - { "dryrun", "Runs the updater but doesn't write the updates to files.", _ => Set(p => p.Parameters.IsDryRun = true) }, - { "result|r=", "The path to the file where the update result should be saved.", s => context.ResultFile = s }, - { "versionOverrides=", "The path to a JSON file to force specifc versions to be used; format should be the same as the result file", s => Set(p => p.Parameters.VersionOverrides.AddRange(LoadManualOperations(s))) }, + { "help|h", "Displays this help screen", TrySet(_ => context.IsHelp = true) }, + { "solution=|s=", "The {path} to the solution or folder to update; defaults to the current folder", TrySet(x => context.Parameters.SolutionRoot = x) }, + { "feed=|f=", "A NuGet feed to use for the update; a private feed can be specified with the format {url|accessToken}; can be specified multiple times", TryParseAndSet(PackageFeed.FromString, x => context.Parameters.Feeds.Add(x)) }, + { "version=|versions=|v=", "The target {version} to use; latest stable is always considered; can be specified multiple times", TrySet(x => context.Parameters.TargetVersions.Add(x)) }, + { "ignorePackages=|ignore=|i=", "A specific {package} to ignore; can be specified multiple times", TrySet(x => context.Parameters.PackagesToIgnore.Add(x)) }, + { "updatePackages=|update=|u=", "A specific {package} to update; not specifying this will update all packages found; can be specified multiple times", TrySet(x => context.Parameters.PackagesToUpdate.Add(x)) }, + { "packageAuthor=|a=", "The {author} of the packages to update; used for public packages only", TrySet(x => context.Parameters.PackageAuthor = x)}, + { "outputFile=|of=", "The {path} to a markdown file where the update summary will be written", TrySet(x => context.SummaryFile = x) }, + { "allowDowngrade|d", "Whether package downgrade is allowed", TrySet(x => context.Parameters.IsDowngradeAllowed = true)}, + { "useNuGetorg|n", "Whether to use packages from NuGet.org", TrySet(_ => context.Parameters.Feeds.Add(PackageFeed.NuGetOrg)) }, + { "silent", "Suppress all output from NuGet Updater", TrySet(_ => context.IsSilent = true) }, + { "strict", "Whether to use versions with only the specified version tag (ie. dev, but not dev.test)", TrySet(_ => context.Parameters.Strict = true) }, + { "dryrun", "Runs the updater but doesn't write the updates to files.", TrySet(_ => context.Parameters.IsDryRun = true) }, + { "result|r=", "The path to the file where the update result should be saved.", TrySet(x => context.ResultFile = x) }, + { "versionOverrides=", "The path to a JSON file to force specifc versions to be used; format should be the same as the result file", TryParseAndSet(LoadManualOperations, x => context.Parameters.VersionOverrides.AddRange(x)) }, }; - void Set(Action setter) => context?.Apply(setter); + Action TrySet(Action set) + { + return value => + { + if (context != null) + { + try + { + set(value); + } + catch(Exception e) + { + context.Errors.Add(new ConsoleArgError(value, ConsoleArgError.ErrorType.ValueAssignmentError, e)); + } + } + }; + } + + Action TryParseAndSet(Func parse, Action set) + { + return value => + { + if(context != null) + { + var isParsing = true; + try + { + var parsed = parse(value); + isParsing = false; + set(parsed); + } + catch(Exception e) + { + context.Errors.Add(new ConsoleArgError( + value, + isParsing ? ConsoleArgError.ErrorType.ValueParsingError : ConsoleArgError.ErrorType.ValueAssignmentError, + e + )); + } + } + }; + } } public static ConsoleArgsContext Parse(string[] args) @@ -51,7 +92,7 @@ public static ConsoleArgsContext Parse(string[] args) Parameters = new UpdaterParameters { UpdateTarget = FileType.All }, }; var unparsed = CreateOptionsFor(context).Parse(args); - context.Errors.AddRange(unparsed.Select(ConsoleArgError.UnrecognizedArgument)); + context.Errors.AddRange(unparsed.Select(x => new ConsoleArgError(x, ConsoleArgError.ErrorType.UnrecognizedArgument))); return context; } @@ -102,15 +143,18 @@ public ConsoleArgError(string argument, ErrorType type, Exception e = null) public string Message => Type switch { ErrorType.UnrecognizedArgument => "unrecognized argument: " + Argument, + ErrorType.ValueAssignmentError => "error while trying to assign value: " + Argument, + ErrorType.ValueParsingError => "error while trying to parse value: " + Argument, + _ => $"{Type}: " + Argument, }; - internal static ConsoleArgError UnrecognizedArgument(string argument) => new ConsoleArgError(argument, ErrorType.UnrecognizedArgument); - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Self-Explantory")] public enum ErrorType { UnrecognizedArgument, + ValueAssignmentError, + ValueParsingError, } } } From 339b7d8bbb5466951aff213d7122e7eee7bca6da Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Tue, 11 Feb 2020 13:35:38 -0500 Subject: [PATCH 5/9] refactored nested classes --- .../ConsoleArgsParserTests.cs | 23 ++--- .../Arguments/ConsoleArgError.cs | 31 ++++++ .../Arguments/ConsoleArgErrorType.cs | 13 +++ .../ConsoleArgsContext.Properties.cs | 27 ++++++ .../ConsoleArgsContext.cs} | 94 +++++-------------- src/NuGet.Updater.Tool/Program.cs | 9 +- 6 files changed, 108 insertions(+), 89 deletions(-) create mode 100644 src/NuGet.Updater.Tool/Arguments/ConsoleArgError.cs create mode 100644 src/NuGet.Updater.Tool/Arguments/ConsoleArgErrorType.cs create mode 100644 src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.Properties.cs rename src/NuGet.Updater.Tool/{ConsoleArgsParser.cs => Arguments/ConsoleArgsContext.cs} (69%) diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs index 12a720f..df8abd0 100644 --- a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -3,18 +3,20 @@ using System.IO; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; -using NuGet.Updater.Tool; +using NuGet.Updater.Tool.Arguments; namespace NuGet.Updater.Tests { [TestClass] public class ConsoleArgsParserTests { + private const string NotExistingFilePath = @"c:\not\existing\file.mia"; + [TestMethod] public void Given_HelpArgument_ContextIsHelp() { var arguments = new[] { "-help" }; - var context = ConsoleArgsParser.Parse(arguments); + var context = ConsoleArgsContext.Parse(arguments); Assert.IsTrue(context.IsHelp); } @@ -23,11 +25,11 @@ public void Given_HelpArgument_ContextIsHelp() public void Given_UnrecognizedArgument_ContextHasError() { var arguments = new[] { "--absolutelyWrong" }; - var context = ConsoleArgsParser.Parse(arguments); + var context = ConsoleArgsContext.Parse(arguments); Assert.IsTrue(context.HasError); Assert.AreEqual(context.Errors[0].Argument, arguments[0]); - Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.UnrecognizedArgument); + Assert.AreEqual(context.Errors[0].Type, ConsoleArgErrorType.UnrecognizedArgument); } [TestMethod] @@ -35,23 +37,22 @@ public void Given_UnrecognizedArgument_ContextHasError() public void Given_UnrecognizedArgument_ContextHasError2() { var arguments = new[] { "-asd" }; - var context = ConsoleArgsParser.Parse(arguments); + var context = ConsoleArgsContext.Parse(arguments); Assert.IsTrue(context.HasError); Assert.AreEqual(context.Errors[0].Argument, arguments[0]); - Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.UnrecognizedArgument); + Assert.AreEqual(context.Errors[0].Type, ConsoleArgErrorType.UnrecognizedArgument); } [TestMethod] public void Given_InvalidArgumentParameter_ContextHasError() { - const string MissingFile = @"c:\not\existing\file.mia"; - var arguments = new[] { $"--versionOverrides={MissingFile}" }; - var context = ConsoleArgsParser.Parse(arguments); + var arguments = new[] { $"--versionOverrides={NotExistingFilePath}" }; + var context = ConsoleArgsContext.Parse(arguments); Assert.IsTrue(context.HasError); - Assert.AreEqual(context.Errors[0].Argument, MissingFile); - Assert.AreEqual(context.Errors[0].Type, ConsoleArgsParser.ConsoleArgError.ErrorType.ValueParsingError); + Assert.AreEqual(context.Errors[0].Argument, NotExistingFilePath); + Assert.AreEqual(context.Errors[0].Type, ConsoleArgErrorType.ValueParsingError); Assert.IsInstanceOfType(context.Errors[0].Exception, typeof(DirectoryNotFoundException)); } } diff --git a/src/NuGet.Updater.Tool/Arguments/ConsoleArgError.cs b/src/NuGet.Updater.Tool/Arguments/ConsoleArgError.cs new file mode 100644 index 0000000..0ca16a2 --- /dev/null +++ b/src/NuGet.Updater.Tool/Arguments/ConsoleArgError.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NuGet.Updater.Tool.Arguments +{ + public class ConsoleArgError + { + public ConsoleArgErrorType Type { get; set; } + + public string Argument { get; set; } + + public Exception Exception { get; set; } + + public ConsoleArgError(string argument, ConsoleArgErrorType type, Exception e = null) + { + Argument = argument; + Type = type; + Exception = e; + } + + public string Message => Type switch + { + ConsoleArgErrorType.UnrecognizedArgument => "unrecognized argument: " + Argument, + ConsoleArgErrorType.ValueAssignmentError => "error while trying to assign value: " + Argument, + ConsoleArgErrorType.ValueParsingError => "error while trying to parse value: " + Argument, + + _ => $"{Type}: " + Argument, + }; + } +} diff --git a/src/NuGet.Updater.Tool/Arguments/ConsoleArgErrorType.cs b/src/NuGet.Updater.Tool/Arguments/ConsoleArgErrorType.cs new file mode 100644 index 0000000..a94821d --- /dev/null +++ b/src/NuGet.Updater.Tool/Arguments/ConsoleArgErrorType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NuGet.Updater.Tool.Arguments +{ + public enum ConsoleArgErrorType + { + UnrecognizedArgument, + ValueAssignmentError, + ValueParsingError, + } +} diff --git a/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.Properties.cs b/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.Properties.cs new file mode 100644 index 0000000..7cd60a2 --- /dev/null +++ b/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.Properties.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NuGet.Updater.Entities; + +namespace NuGet.Updater.Tool.Arguments +{ + public partial class ConsoleArgsContext + { + private ConsoleArgsContext() { } + + public bool HasError => Errors.Any(); + + public IList Errors { get; } = new List(); + + public bool IsHelp { get; set; } + + public bool IsSilent { get; set; } + + public string SummaryFile { get; set; } + + public string ResultFile { get; set; } + + public UpdaterParameters Parameters { get; set; } + } +} diff --git a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs b/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs similarity index 69% rename from src/NuGet.Updater.Tool/ConsoleArgsParser.cs rename to src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs index 40c352d..2e671fb 100644 --- a/src/NuGet.Updater.Tool/ConsoleArgsParser.cs +++ b/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs @@ -9,13 +9,28 @@ using NuGet.Versioning; using Uno.Extensions; -namespace NuGet.Updater.Tool +namespace NuGet.Updater.Tool.Arguments { - public static class ConsoleArgsParser + public partial class ConsoleArgsContext { - public static OptionSet GetOptions() => CreateOptionsFor(default); + public static ConsoleArgsContext Parse(string[] args) + { + if(args.Length == 0) + { + return new ConsoleArgsContext { IsHelp = true }; + } + + var context = new ConsoleArgsContext + { + Parameters = new UpdaterParameters { UpdateTarget = FileType.All }, + }; + var unparsed = CreateOptionsFor(context).Parse(args); + context.Errors.AddRange(unparsed.Select(x => new ConsoleArgError(x, ConsoleArgErrorType.UnrecognizedArgument))); + + return context; + } - private static OptionSet CreateOptionsFor(ConsoleArgsContext context = null) + internal static OptionSet CreateOptionsFor(ConsoleArgsContext context = null) { return new OptionSet { @@ -48,7 +63,7 @@ Action TrySet(Action set) } catch(Exception e) { - context.Errors.Add(new ConsoleArgError(value, ConsoleArgError.ErrorType.ValueAssignmentError, e)); + context.Errors.Add(new ConsoleArgError(value, ConsoleArgErrorType.ValueAssignmentError, e)); } } }; @@ -71,7 +86,7 @@ Action TryParseAndSet(Func parse, Action set) { context.Errors.Add(new ConsoleArgError( value, - isParsing ? ConsoleArgError.ErrorType.ValueParsingError : ConsoleArgError.ErrorType.ValueAssignmentError, + isParsing ? ConsoleArgErrorType.ValueParsingError : ConsoleArgErrorType.ValueAssignmentError, e )); } @@ -80,22 +95,7 @@ Action TryParseAndSet(Func parse, Action set) } } - public static ConsoleArgsContext Parse(string[] args) - { - if (args.Empty()) - { - return new ConsoleArgsContext { IsHelp = true }; - } - - var context = new ConsoleArgsContext - { - Parameters = new UpdaterParameters { UpdateTarget = FileType.All }, - }; - var unparsed = CreateOptionsFor(context).Parse(args); - context.Errors.AddRange(unparsed.Select(x => new ConsoleArgError(x, ConsoleArgError.ErrorType.UnrecognizedArgument))); - - return context; - } + public void WriteOptionDescriptions(TextWriter writer) => CreateOptionsFor(default).WriteOptionDescriptions(writer); private static Dictionary LoadManualOperations(string inputFilePath) { @@ -107,55 +107,5 @@ private static Dictionary LoadManualOperations(string inpu return result.ToDictionary(r => r.PackageId, r => new NuGetVersion(r.UpdatedVersion)); } } - - public class ConsoleArgsContext - { - public bool HasError => Errors.Any(); - - public IList Errors { get; } = new List(); - - public bool IsHelp { get; set; } - - public bool IsSilent { get; set; } - - public string SummaryFile { get; set; } - - public string ResultFile { get; set; } - - public UpdaterParameters Parameters { get; set; } - } - - public class ConsoleArgError - { - public ErrorType Type { get; set; } - - public string Argument { get; set; } - - public Exception Exception { get; set; } - - public ConsoleArgError(string argument, ErrorType type, Exception e = null) - { - Argument = argument; - Type = type; - Exception = e; - } - - public string Message => Type switch - { - ErrorType.UnrecognizedArgument => "unrecognized argument: " + Argument, - ErrorType.ValueAssignmentError => "error while trying to assign value: " + Argument, - ErrorType.ValueParsingError => "error while trying to parse value: " + Argument, - - _ => $"{Type}: " + Argument, - }; - - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Self-Explantory")] - public enum ErrorType - { - UnrecognizedArgument, - ValueAssignmentError, - ValueParsingError, - } - } } } diff --git a/src/NuGet.Updater.Tool/Program.cs b/src/NuGet.Updater.Tool/Program.cs index b6d8a0d..da63c0b 100644 --- a/src/NuGet.Updater.Tool/Program.cs +++ b/src/NuGet.Updater.Tool/Program.cs @@ -4,14 +4,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Mono.Options; using Newtonsoft.Json; -using NuGet.Packaging; -using NuGet.Shared.Entities; using NuGet.Shared.Helpers; using NuGet.Shared.Log; using NuGet.Updater.Entities; -using NuGet.Versioning; +using NuGet.Updater.Tool.Arguments; namespace NuGet.Updater.Tool { @@ -21,7 +18,7 @@ public static async Task Main(string[] args) { try { - var context = ConsoleArgsParser.Parse(args); + var context = ConsoleArgsContext.Parse(args); if (context.HasError) { @@ -32,7 +29,7 @@ public static async Task Main(string[] args) { Console.WriteLine("NuGet Updater is a tool allowing the automatic update of the NuGet packages found in a solution"); Console.WriteLine(); - ConsoleArgsParser.GetOptions().WriteOptionDescriptions(Console.Out); + context.WriteOptionDescriptions(Console.Out); } else { From 2c7acc63325f23981d2db8aa5fc6096a080d124b Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Tue, 11 Feb 2020 13:35:57 -0500 Subject: [PATCH 6/9] ignored invalid test --- src/NuGet.Updater.Tests/SolutionHelperTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NuGet.Updater.Tests/SolutionHelperTests.cs b/src/NuGet.Updater.Tests/SolutionHelperTests.cs index 25ee5ee..5c13d4d 100644 --- a/src/NuGet.Updater.Tests/SolutionHelperTests.cs +++ b/src/NuGet.Updater.Tests/SolutionHelperTests.cs @@ -10,6 +10,7 @@ namespace NuGet.Updater.Tests [TestClass] public class SolutionHelperTests { + [Ignore("hardcoded local path")] [TestMethod] public async Task GivenSolution_PackageReferencesAreFound() { From 310aa7aed7e6747231b429c28f6f84865b1f79db Mon Sep 17 00:00:00 2001 From: Xiaoy312 Date: Fri, 14 Feb 2020 11:10:03 -0500 Subject: [PATCH 7/9] added more tests for cli parameters --- .../ConsoleArgsParserTests.cs | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs index df8abd0..c6f093b 100644 --- a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NuGet.Updater.Entities; using NuGet.Updater.Tool.Arguments; namespace NuGet.Updater.Tests @@ -11,6 +12,7 @@ namespace NuGet.Updater.Tests public class ConsoleArgsParserTests { private const string NotExistingFilePath = @"c:\not\existing\file.mia"; + private const string SomeText = nameof(SomeText); [TestMethod] public void Given_HelpArgument_ContextIsHelp() @@ -55,5 +57,65 @@ public void Given_InvalidArgumentParameter_ContextHasError() Assert.AreEqual(context.Errors[0].Type, ConsoleArgErrorType.ValueParsingError); Assert.IsInstanceOfType(context.Errors[0].Exception, typeof(DirectoryNotFoundException)); } + + [DataTestMethod] + [DataRow(nameof(ConsoleArgsContext.IsHelp), "--help", true)] + [DataRow(nameof(ConsoleArgsContext.IsHelp), "-h", true)] + [DataRow(nameof(ConsoleArgsContext.IsSilent), "--silent", true)] + [DataRow(nameof(ConsoleArgsContext.ResultFile), "--result=" + NotExistingFilePath, NotExistingFilePath)] + [DataRow(nameof(ConsoleArgsContext.ResultFile), "-r=" + NotExistingFilePath, NotExistingFilePath)] + [DataRow(nameof(ConsoleArgsContext.SummaryFile), "--outputFile=" + NotExistingFilePath, NotExistingFilePath)] + [DataRow(nameof(ConsoleArgsContext.SummaryFile), "-of=" + NotExistingFilePath, NotExistingFilePath)] + public void Given_ContextArgument_ContextPropertyIsSet(string propertyName, string argument, object expectedValue) + { + Func propertySelector = propertyName switch + { + nameof(ConsoleArgsContext.IsHelp) => x => x.IsHelp, + nameof(ConsoleArgsContext.IsSilent) => x => x.IsSilent, + nameof(ConsoleArgsContext.ResultFile) => x => x.ResultFile, + nameof(ConsoleArgsContext.SummaryFile) => x => x.SummaryFile, + + _ => throw new ArgumentOutOfRangeException(argument), + }; + + var arguments = new[] { argument }; + var context = ConsoleArgsContext.Parse(arguments); + + Assert.IsFalse(context.HasError); + + var actualValue = propertySelector(context); + Assert.AreEqual(expectedValue, actualValue); + } + + [DataTestMethod] + [DataRow(nameof(UpdaterParameters.SolutionRoot), "--solution=" + NotExistingFilePath, NotExistingFilePath)] + [DataRow(nameof(UpdaterParameters.SolutionRoot), "-s=" + NotExistingFilePath, NotExistingFilePath)] + [DataRow(nameof(UpdaterParameters.PackageAuthor), "--packageAuthor=" + SomeText, SomeText)] + [DataRow(nameof(UpdaterParameters.PackageAuthor), "-a=" + SomeText, SomeText)] + [DataRow(nameof(UpdaterParameters.IsDowngradeAllowed), "--allowDowngrade", true)] + [DataRow(nameof(UpdaterParameters.IsDowngradeAllowed), "-d", true)] + [DataRow(nameof(UpdaterParameters.Strict), "--strict", true)] + [DataRow(nameof(UpdaterParameters.IsDryRun), "--dryrun", true)] + public void Given_UpdaterParametersArgument_UpdaterParametersPropertyIsSet(string propertyName, string argument, object expectedValue) + { + Func propertySelector = propertyName switch + { + nameof(UpdaterParameters.SolutionRoot) => x => x.SolutionRoot, + nameof(UpdaterParameters.PackageAuthor) => x => x.PackageAuthor, + nameof(UpdaterParameters.IsDowngradeAllowed) => x => x.IsDowngradeAllowed, + nameof(UpdaterParameters.Strict) => x => x.Strict, + nameof(UpdaterParameters.IsDryRun) => x => x.IsDryRun, + + _ => throw new ArgumentOutOfRangeException(propertyName), + }; + + var arguments = new[] { argument }; + var context = ConsoleArgsContext.Parse(arguments); + + Assert.IsFalse(context.HasError); + + var actualValue = propertySelector(context.Parameters); + Assert.AreEqual(expectedValue, actualValue); + } } } From 45861ff9d2c8160bf8c10f93b01abe259fc5d335 Mon Sep 17 00:00:00 2001 From: Xiaotian Gu Date: Mon, 17 Feb 2020 15:00:02 -0500 Subject: [PATCH 8/9] added test for cli parameters of collection type --- src/NuGet.Shared/Entities/PackageFeed.cs | 15 +++++++ .../ConsoleArgsParserTests.cs | 39 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/NuGet.Shared/Entities/PackageFeed.cs b/src/NuGet.Shared/Entities/PackageFeed.cs index 9b9d7d5..10306a3 100644 --- a/src/NuGet.Shared/Entities/PackageFeed.cs +++ b/src/NuGet.Shared/Entities/PackageFeed.cs @@ -113,5 +113,20 @@ public async Task PushPackage(CancellationToken ct, LocalPackage package) await _packageSource.PushPackage(ct, package, Logger); } + + public override int GetHashCode() => _packageSource.GetHashCode(); + + public override bool Equals(object obj) + { + if(obj is PackageFeed other) + { + if(_packageSource != null && other?._packageSource != null) + { + return this._packageSource.Equals(other._packageSource); + } + } + + return base.Equals(obj); + } } } diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs index c6f093b..1123b58 100644 --- a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -1,8 +1,12 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Linq.Expressions; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NuGet.Shared.Entities; using NuGet.Updater.Entities; using NuGet.Updater.Tool.Arguments; @@ -13,6 +17,8 @@ public class ConsoleArgsParserTests { private const string NotExistingFilePath = @"c:\not\existing\file.mia"; private const string SomeText = nameof(SomeText); + private const string SomePublicFeed = "https://pkgs.dev.azure.com/qwe/_packaging/asd/nuget/v3/index.json"; + private const string SomePrivateFeed = "https://pkgs.dev.azure.com/qwe/_packaging/asd/nuget/v3/index.json|hunter2"; [TestMethod] public void Given_HelpArgument_ContextIsHelp() @@ -117,5 +123,38 @@ public void Given_UpdaterParametersArgument_UpdaterParametersPropertyIsSet(strin var actualValue = propertySelector(context.Parameters); Assert.AreEqual(expectedValue, actualValue); } + + private static IEnumerable CollectionPropertiesTestSetup() => new (Expression>, string, object)[] + { + ( x => x.Feeds, "--useNuGetorg", PackageFeed.NuGetOrg ), + ( x => x.Feeds, "-n", PackageFeed.NuGetOrg ), + ( x => x.Feeds, "--feed=" + SomePublicFeed, PackageFeed.FromString(SomePublicFeed) ), + ( x => x.Feeds, "--feed=" + SomePrivateFeed, PackageFeed.FromString(SomePrivateFeed) ), + ( x => x.Feeds, "-f=" + SomePublicFeed, PackageFeed.FromString(SomePublicFeed) ), + ( x => x.Feeds, "-f=" + SomePrivateFeed, PackageFeed.FromString(SomePrivateFeed) ), + ( x => x.PackagesToUpdate, "--updatePackages=" + SomeText, SomeText ), + ( x => x.PackagesToUpdate, "--update=" + SomeText, SomeText ), + ( x => x.PackagesToUpdate, "-u=" + SomeText, SomeText ), + ( x => x.PackagesToIgnore, "--ignorePackages=" + SomeText, SomeText ), + ( x => x.PackagesToIgnore, "--ignore=" + SomeText, SomeText ), + ( x => x.PackagesToIgnore, "-i=" + SomeText, SomeText ), + ( x => x.TargetVersions, "--versions=" + SomeText, SomeText ), + ( x => x.TargetVersions, "--version=" + SomeText, SomeText ), + ( x => x.TargetVersions, "-v=" + SomeText, SomeText ), + }.Select(x => new[] { x.Item1, x.Item2, x.Item3 }); + + [DataTestMethod] + [DynamicData(nameof(CollectionPropertiesTestSetup), DynamicDataSourceType.Method)] + public void Given_UpdaterParametersArgument_ContextCollectionPropertyIsSet(Expression> propertySelector, string argument, object expectedValue) + { + var arguments = new[] { argument }; + var context = ConsoleArgsContext.Parse(arguments); + + Assert.IsFalse(context.HasError); + + var collection = propertySelector.Compile()(context.Parameters); + var actualValue = collection?.Cast().FirstOrDefault(); + Assert.AreEqual(expectedValue, actualValue); + } } } From c63c000261224beb9067e5c5872e39c4c568b772 Mon Sep 17 00:00:00 2001 From: Xiaotian Gu Date: Mon, 17 Feb 2020 16:44:52 -0500 Subject: [PATCH 9/9] added test for cli parameter: versionOverrides --- .../ConsoleArgsParserTests.cs | 17 +++++++++++++++++ .../NuGet.Updater.Tests.csproj | 6 ++++++ .../Resources/version_overrides.json | 4 ++++ .../Arguments/ConsoleArgsContext.cs | 2 +- .../NuGet.Updater.Tool.csproj | 6 ++++++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/NuGet.Updater.Tests/Resources/version_overrides.json diff --git a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs index 1123b58..1b26b14 100644 --- a/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs +++ b/src/NuGet.Updater.Tests/ConsoleArgsParserTests.cs @@ -19,6 +19,7 @@ public class ConsoleArgsParserTests private const string SomeText = nameof(SomeText); private const string SomePublicFeed = "https://pkgs.dev.azure.com/qwe/_packaging/asd/nuget/v3/index.json"; private const string SomePrivateFeed = "https://pkgs.dev.azure.com/qwe/_packaging/asd/nuget/v3/index.json|hunter2"; + private const string PinnedVersionJsonPath = @"Resources\version_overrides.json"; [TestMethod] public void Given_HelpArgument_ContextIsHelp() @@ -156,5 +157,21 @@ public void Given_UpdaterParametersArgument_ContextCollectionPropertyIsSet(Expre var actualValue = collection?.Cast().FirstOrDefault(); Assert.AreEqual(expectedValue, actualValue); } + + [TestMethod] + [DeploymentItem(PinnedVersionJsonPath)] + public void Given_UpdaterParametersArgument_ContextTargetVersionIsSet() + { + var arguments = new[] { "--versionOverrides=" + PinnedVersionJsonPath }; + var context = ConsoleArgsContext.Parse(arguments); + + Assert.IsFalse(context.HasError); + + var actualValues = context.Parameters.VersionOverrides + .ToDictionary(x => x.Key, x => x.Value); + var expectedValues = ConsoleArgsContext.LoadManualOperations(PinnedVersionJsonPath); + + CollectionAssert.AreEqual(expectedValues, actualValues); + } } } diff --git a/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj b/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj index 6d03a73..4d4e4f1 100644 --- a/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj +++ b/src/NuGet.Updater.Tests/NuGet.Updater.Tests.csproj @@ -17,4 +17,10 @@ + + + PreserveNewest + + + diff --git a/src/NuGet.Updater.Tests/Resources/version_overrides.json b/src/NuGet.Updater.Tests/Resources/version_overrides.json new file mode 100644 index 0000000..8c9164d --- /dev/null +++ b/src/NuGet.Updater.Tests/Resources/version_overrides.json @@ -0,0 +1,4 @@ +[ + { "PackageId": "Newtonsoft.Json", "UpdatedVersion": "12.0.1" }, + { "PackageId": "Microsoft.Extensions.Logging", "UpdatedVersion": "3.1.2" } +] diff --git a/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs b/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs index 2e671fb..67f97f8 100644 --- a/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs +++ b/src/NuGet.Updater.Tool/Arguments/ConsoleArgsContext.cs @@ -97,7 +97,7 @@ Action TryParseAndSet(Func parse, Action set) public void WriteOptionDescriptions(TextWriter writer) => CreateOptionsFor(default).WriteOptionDescriptions(writer); - private static Dictionary LoadManualOperations(string inputFilePath) + internal static Dictionary LoadManualOperations(string inputFilePath) { using(var fileReader = File.OpenText(inputFilePath)) using(var jsonReader = new JsonTextReader(fileReader)) diff --git a/src/NuGet.Updater.Tool/NuGet.Updater.Tool.csproj b/src/NuGet.Updater.Tool/NuGet.Updater.Tool.csproj index fcc8cf2..2b2a8e8 100644 --- a/src/NuGet.Updater.Tool/NuGet.Updater.Tool.csproj +++ b/src/NuGet.Updater.Tool/NuGet.Updater.Tool.csproj @@ -35,6 +35,12 @@ + + + <_Parameter1>NuGet.Updater.Tests + + +