diff --git a/src/Microsoft.DotNet.Watcher.Tools/CommandLineOptions.cs b/src/Microsoft.DotNet.Watcher.Tools/CommandLineOptions.cs index b148ad6f08..aea719428d 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/CommandLineOptions.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/CommandLineOptions.cs @@ -1,12 +1,11 @@ -// 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.IO; using Microsoft.DotNet.Watcher.Tools; -using Microsoft.DotNet.Watcher.Internal; using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher { @@ -17,7 +16,7 @@ namespace Microsoft.DotNet.Watcher public bool IsQuiet { get; private set; } public bool IsVerbose { get; private set; } public IList RemainingArguments { get; private set; } - public static CommandLineOptions Parse(string[] args, TextWriter stdout, TextWriter stderr) + public static CommandLineOptions Parse(string[] args, IConsole console) { Ensure.NotNull(args, nameof(args)); @@ -25,8 +24,8 @@ namespace Microsoft.DotNet.Watcher { Name = "dotnet watch", FullName = "Microsoft DotNet File Watcher", - Out = stdout, - Error = stderr, + Out = console.Out, + Error = console.Error, AllowArgumentSeparator = true, ExtendedHelpText = @" Environment variables: @@ -80,7 +79,7 @@ Examples: if (optQuiet.HasValue() && optVerbose.HasValue()) { - stderr.WriteLine(Resources.Error_QuietAndVerboseSpecified.Bold().Red()); + console.Error.WriteLine(Resources.Error_QuietAndVerboseSpecified.Bold().Red()); return null; } diff --git a/src/Microsoft.DotNet.Watcher.Tools/Internal/FileSetWatcher.cs b/src/Microsoft.DotNet.Watcher.Tools/Internal/FileSetWatcher.cs index b97b547a52..597d13cdd1 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Internal/FileSetWatcher.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Internal/FileSetWatcher.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher.Internal { diff --git a/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/DotnetFileWatcher.cs b/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/DotnetFileWatcher.cs index 4cda4f0cf8..5064239746 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/DotnetFileWatcher.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/DotnetFileWatcher.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher.Internal { diff --git a/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/PollingFileWatcher.cs b/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/PollingFileWatcher.cs index d26dcc54f2..25e4f6f76f 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/PollingFileWatcher.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Internal/FileWatcher/PollingFileWatcher.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher.Internal { diff --git a/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildFileSetFactory.cs b/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildFileSetFactory.cs index e332ad88f5..282b15b8e6 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildFileSetFactory.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildFileSetFactory.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/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildProjectFinder.cs b/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildProjectFinder.cs index 2eefb9a891..b6cc515aec 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildProjectFinder.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Internal/MsBuildProjectFinder.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; using Microsoft.DotNet.Watcher.Tools; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher.Internal { diff --git a/src/Microsoft.DotNet.Watcher.Tools/Program.cs b/src/Microsoft.DotNet.Watcher.Tools/Program.cs index bb1bbfd828..16a7a17288 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Program.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Program.cs @@ -16,34 +16,36 @@ namespace Microsoft.DotNet.Watcher public class Program { private const string LoggerName = "DotNetWatcher"; - private readonly CancellationToken _cancellationToken; - private readonly TextWriter _stdout; - private readonly TextWriter _stderr; + private readonly IConsole _console; private readonly string _workingDir; - public Program(TextWriter consoleOutput, TextWriter consoleError, string workingDir, CancellationToken cancellationToken) + public Program(IConsole console, string workingDir) { - Ensure.NotNull(consoleOutput, nameof(consoleOutput)); - Ensure.NotNull(consoleError, nameof(consoleError)); + Ensure.NotNull(console, nameof(console)); Ensure.NotNullOrEmpty(workingDir, nameof(workingDir)); - _cancellationToken = cancellationToken; - _stdout = consoleOutput; - _stderr = consoleError; + _console = console; _workingDir = workingDir; } public static int Main(string[] args) { HandleDebugSwitch(ref args); + return new Program(PhysicalConsole.Singleton, Directory.GetCurrentDirectory()) + .RunAsync(args) + .GetAwaiter() + .GetResult(); + } + public async Task RunAsync(string[] args) + { using (CancellationTokenSource ctrlCTokenSource = new CancellationTokenSource()) { - Console.CancelKeyPress += (sender, ev) => + _console.CancelKeyPress += (sender, ev) => { if (!ctrlCTokenSource.IsCancellationRequested) { - Console.WriteLine($"[{LoggerName}] Shutdown requested. Press Ctrl+C again to force exit."); + _console.Out.WriteLine($"[{LoggerName}] Shutdown requested. Press Ctrl+C again to force exit."); ev.Cancel = true; } else @@ -55,10 +57,7 @@ namespace Microsoft.DotNet.Watcher try { - return new Program(Console.Out, Console.Error, Directory.GetCurrentDirectory(), ctrlCTokenSource.Token) - .MainInternalAsync(args) - .GetAwaiter() - .GetResult(); + return await MainInternalAsync(args, ctrlCTokenSource.Token); } catch (Exception ex) { @@ -68,16 +67,16 @@ namespace Microsoft.DotNet.Watcher return 0; } - Console.Error.WriteLine(ex.ToString()); - Console.Error.WriteLine($"[{LoggerName}] An unexpected error occurred".Bold().Red()); + _console.Error.WriteLine(ex.ToString()); + _console.Error.WriteLine($"[{LoggerName}] An unexpected error occurred".Bold().Red()); return 1; } } } - private async Task MainInternalAsync(string[] args) + private async Task MainInternalAsync(string[] args, CancellationToken cancellationToken) { - var options = CommandLineOptions.Parse(args, _stdout, _stdout); + var options = CommandLineOptions.Parse(args, _console); if (options == null) { // invalid args syntax @@ -105,7 +104,7 @@ namespace Microsoft.DotNet.Watcher } catch (FileNotFoundException ex) { - _stderr.WriteLine(ex.Message.Bold().Red()); + _console.Error.WriteLine(ex.Message.Bold().Red()); return 1; } @@ -119,7 +118,7 @@ namespace Microsoft.DotNet.Watcher }; await new DotNetWatcher(logger) - .WatchAsync(processInfo, fileSetFactory, _cancellationToken); + .WatchAsync(processInfo, fileSetFactory, cancellationToken); return 0; } diff --git a/src/Microsoft.DotNet.Watcher.Tools/Properties/Resources.Designer.cs b/src/Microsoft.DotNet.Watcher.Tools/Properties/Resources.Designer.cs index 30c1d18cb7..ee248b1342 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Properties/Resources.Designer.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Properties/Resources.Designer.cs @@ -74,22 +74,6 @@ namespace Microsoft.DotNet.Watcher.Tools return GetString("Error_QuietAndVerboseSpecified"); } - /// - /// Value cannot be null or an empty string. - /// - internal static string Error_StringNullOrEmpty - { - get { return GetString("Error_StringNullOrEmpty"); } - } - - /// - /// Value cannot be null or an empty string. - /// - internal static string FormatError_StringNullOrEmpty() - { - return GetString("Error_StringNullOrEmpty"); - } - private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.DotNet.Watcher.Tools/Resources.resx b/src/Microsoft.DotNet.Watcher.Tools/Resources.resx index 125a4cfea3..b66821626b 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Resources.resx +++ b/src/Microsoft.DotNet.Watcher.Tools/Resources.resx @@ -129,7 +129,4 @@ Cannot specify both '--quiet' and '--verbose' options. - - Value cannot be null or an empty string. - \ No newline at end of file diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandContext.cs b/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandContext.cs index 9503178930..8ad2ffd9f9 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandContext.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandContext.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.SecretManager.Tools.Internal { diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandLineOptions.cs b/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandLineOptions.cs index 42d826e1e0..2284b2ed25 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandLineOptions.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Internal/CommandLineOptions.cs @@ -4,6 +4,7 @@ using System.Reflection; using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.SecretManager.Tools.Internal { diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/MsBuildProjectFinder.cs b/src/Microsoft.Extensions.SecretManager.Tools/Internal/MsBuildProjectFinder.cs index e080b4cb83..ed37e7ad87 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/MsBuildProjectFinder.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Internal/MsBuildProjectFinder.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; using Microsoft.DotNet.Cli.Utils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.SecretManager.Tools.Internal { @@ -14,10 +15,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal public MsBuildProjectFinder(string directory) { - if (string.IsNullOrEmpty(directory)) - { - throw new ArgumentException(Resources.Common_StringNullOrEmpty, nameof(directory)); - } + Ensure.NotNullOrEmpty(directory, nameof(directory)); _directory = directory; } diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/SecretsStore.cs b/src/Microsoft.Extensions.SecretManager.Tools/Internal/SecretsStore.cs index 22e03ee6d7..10813b53d4 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/SecretsStore.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Internal/SecretsStore.cs @@ -9,6 +9,7 @@ using System.Text; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Tools.Internal; using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.SecretManager.Tools.Internal @@ -20,10 +21,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal public SecretsStore(string userSecretsId, ILogger logger) { - if (userSecretsId == null) - { - throw new ArgumentNullException(nameof(userSecretsId)); - } + Ensure.NotNull(userSecretsId, nameof(userSecretsId)); _secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId); diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Program.cs b/src/Microsoft.Extensions.SecretManager.Tools/Program.cs index 3ef60eb563..810c0a043d 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Program.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Program.cs @@ -6,6 +6,7 @@ using System.IO; using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.SecretManager.Tools.Internal; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.SecretManager.Tools { @@ -39,29 +40,13 @@ namespace Microsoft.Extensions.SecretManager.Tools public ILogger Logger { get { return _logger; } - set - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - _logger = value; - } + set { _logger = Ensure.NotNull(value, nameof(value)); } } public CommandOutputProvider CommandOutputProvider { get { return _loggerProvider; } - set - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - _loggerProvider = value; - } + set { _loggerProvider = Ensure.NotNull(value, nameof(value)); } } public bool TryRun(string[] args, out int returnCode) diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Properties/Resources.Designer.cs b/src/Microsoft.Extensions.SecretManager.Tools/Properties/Resources.Designer.cs index a74bdc6798..a75fc0108f 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Properties/Resources.Designer.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Properties/Resources.Designer.cs @@ -10,22 +10,6 @@ namespace Microsoft.Extensions.SecretManager.Tools private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.SecretManager.Tools.Resources", typeof(Resources).GetTypeInfo().Assembly); - /// - /// Value cannot be null or an empty string. - /// - internal static string Common_StringNullOrEmpty - { - get { return GetString("Common_StringNullOrEmpty"); } - } - - /// - /// Value cannot be null or an empty string. - /// - internal static string FormatCommon_StringNullOrEmpty() - { - return GetString("Common_StringNullOrEmpty"); - } - /// /// Command failed : {message} /// diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Resources.resx b/src/Microsoft.Extensions.SecretManager.Tools/Resources.resx index 3ee74a41e9..c9222930fc 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Resources.resx +++ b/src/Microsoft.Extensions.SecretManager.Tools/Resources.resx @@ -117,9 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Value cannot be null or an empty string. - Command failed : {message} diff --git a/src/Microsoft.DotNet.Watcher.Tools/Internal/Ensure.cs b/src/Shared/Ensure.cs similarity index 79% rename from src/Microsoft.DotNet.Watcher.Tools/Internal/Ensure.cs rename to src/Shared/Ensure.cs index 12bfe5f05d..5cb8ff7ec7 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Internal/Ensure.cs +++ b/src/Shared/Ensure.cs @@ -2,9 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.DotNet.Watcher.Tools; -namespace Microsoft.DotNet.Watcher.Internal +namespace Microsoft.Extensions.Tools.Internal { internal static class Ensure { @@ -22,7 +21,7 @@ namespace Microsoft.DotNet.Watcher.Internal { if (string.IsNullOrEmpty(obj)) { - throw new ArgumentException(Resources.Error_StringNullOrEmpty, paramName); + throw new ArgumentException("Value cannot be null or an empty string.", paramName); } return obj; } diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/IConsole.cs b/src/Shared/IConsole.cs similarity index 76% rename from src/Microsoft.Extensions.SecretManager.Tools/Internal/IConsole.cs rename to src/Shared/IConsole.cs index 819d477106..7f1e5e122a 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/IConsole.cs +++ b/src/Shared/IConsole.cs @@ -1,12 +1,14 @@ // 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.IO; -namespace Microsoft.Extensions.SecretManager.Tools.Internal +namespace Microsoft.Extensions.Tools.Internal { public interface IConsole { + event ConsoleCancelEventHandler CancelKeyPress; TextWriter Out { get; } TextWriter Error { get; } TextReader In { get; } diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Internal/PhysicalConsole.cs b/src/Shared/PhysicalConsole.cs similarity index 65% rename from src/Microsoft.Extensions.SecretManager.Tools/Internal/PhysicalConsole.cs rename to src/Shared/PhysicalConsole.cs index 6be8ff495f..7b1a98f621 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Internal/PhysicalConsole.cs +++ b/src/Shared/PhysicalConsole.cs @@ -4,13 +4,21 @@ using System; using System.IO; -namespace Microsoft.Extensions.SecretManager.Tools.Internal +namespace Microsoft.Extensions.Tools.Internal { public class PhysicalConsole : IConsole { - private PhysicalConsole() { } + private PhysicalConsole() + { + Console.CancelKeyPress += (o, e) => + { + CancelKeyPress?.Invoke(o, e); + }; + } public static IConsole Singleton { get; } = new PhysicalConsole(); + + public event ConsoleCancelEventHandler CancelKeyPress; public TextWriter Error => Console.Error; public TextReader In => Console.In; public TextWriter Out => Console.Out; diff --git a/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/project.json b/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/project.json index 4e28d24c2f..9956ccb244 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/project.json +++ b/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/project.json @@ -15,6 +15,7 @@ "Microsoft.DotNet.InternalAbstractions": "1.0.0", "Microsoft.AspNetCore.Testing": "1.0.0", "Microsoft.DotNet.Watcher.Tools": "1.0.0-*", + "Microsoft.DotNet.Watcher.Tools.Tests": "1.0.0-*", "Microsoft.Extensions.Process.Sources": { "type": "build", "version": "1.0.0" diff --git a/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/test.sh b/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/test.sh index e617f19860..e8cd20269a 100755 --- a/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/test.sh +++ b/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/test.sh @@ -1,3 +1,5 @@ +set -e + dotnet build ../../.build/dotnet/dotnet exec \ diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/AssertEx.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/AssertEx.cs index 48975a9175..aa4f94b3fa 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/AssertEx.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/AssertEx.cs @@ -7,7 +7,7 @@ using System.IO; using System.Linq; using Xunit; -namespace Microsoft.DotNetWatcher.Tools.Tests +namespace Microsoft.DotNet.Watcher.Tools.Tests { public static class AssertEx { diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/CommandLineOptionsTests.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/CommandLineOptionsTests.cs index 2957dfe17a..1deb015023 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/CommandLineOptionsTests.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/CommandLineOptionsTests.cs @@ -4,12 +4,27 @@ using System.IO; using System.Linq; using System.Text; +using Microsoft.Extensions.Tools.Internal; using Xunit; +using Xunit.Abstractions; namespace Microsoft.DotNet.Watcher.Tools.Tests { public class CommandLineOptionsTests { + private readonly IConsole _console; + private readonly StringBuilder _stdout = new StringBuilder(); + private readonly StringBuilder _stderr = new StringBuilder(); + + public CommandLineOptionsTests(ITestOutputHelper output) + { + _console = new TestConsole(output) + { + Out = new StringWriter(_stdout), + Error = new StringWriter(_stderr), + }; + } + [Theory] [InlineData(new object[] { new[] { "-h" } })] [InlineData(new object[] { new[] { "-?" } })] @@ -19,12 +34,10 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests [InlineData(new object[] { new string[0] })] public void HelpArgs(string[] args) { - var stdout = new StringBuilder(); - - var options = CommandLineOptions.Parse(args, new StringWriter(stdout), new StringWriter()); + var options = CommandLineOptions.Parse(args, _console); Assert.True(options.IsHelp); - Assert.Contains("Usage: dotnet watch ", stdout.ToString()); + Assert.Contains("Usage: dotnet watch ", _stdout.ToString()); } [Theory] @@ -34,22 +47,18 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests [InlineData(new[] { "--unrecognized-arg" }, new[] { "--unrecognized-arg" })] public void ParsesRemainingArgs(string[] args, string[] expected) { - var stdout = new StringBuilder(); - - var options = CommandLineOptions.Parse(args, new StringWriter(stdout), new StringWriter()); + var options = CommandLineOptions.Parse(args, _console); Assert.Equal(expected, options.RemainingArguments.ToArray()); Assert.False(options.IsHelp); - Assert.Empty(stdout.ToString()); + Assert.Empty(_stdout.ToString()); } [Fact] public void CannotHaveQuietAndVerbose() { - var sb = new StringBuilder(); - var stderr = new StringWriter(sb); - Assert.Null(CommandLineOptions.Parse(new[] { "--quiet", "--verbose" }, new StringWriter(), stderr)); - Assert.Contains(Resources.Error_QuietAndVerboseSpecified, sb.ToString()); + Assert.Null(CommandLineOptions.Parse(new[] { "--quiet", "--verbose" }, _console)); + Assert.Contains(Resources.Error_QuietAndVerboseSpecified, _stderr.ToString()); } } } diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/MsBuildFileSetFactoryTest.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/MsBuildFileSetFactoryTest.cs index d236d0078c..e196742f15 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/MsBuildFileSetFactoryTest.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/MsBuildFileSetFactoryTest.cs @@ -6,13 +6,12 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Microsoft.DotNet.Watcher; using Microsoft.DotNet.Watcher.Internal; using Microsoft.Extensions.Logging; using Xunit; using Xunit.Abstractions; -namespace Microsoft.DotNetWatcher.Tools.Tests +namespace Microsoft.DotNet.Watcher.Tools.Tests { using ItemSpec = TemporaryCSharpProject.ItemSpec; diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/ProgramTests.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/ProgramTests.cs new file mode 100644 index 0000000000..e00099acfb --- /dev/null +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/ProgramTests.cs @@ -0,0 +1,52 @@ +// 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.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Tools.Internal; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.DotNet.Watcher.Tools.Tests +{ + public class ProgramTests : IDisposable + { + private readonly TemporaryDirectory _tempDir; + private readonly TestConsole _console; + + public ProgramTests(ITestOutputHelper output) + { + _tempDir = new TemporaryDirectory(); + _console = new TestConsole(output); + } + + [Fact] + public async Task ConsoleCancelKey() + { + _tempDir + .WithCSharpProject("testproj") + .WithTargetFrameworks("netcoreapp1.0") + .Dir() + .WithFile("Program.cs") + .Create(); + + var stdout = new StringBuilder(); + _console.Out = new StringWriter(stdout); + var program = new Program(_console, _tempDir.Root) + .RunAsync(new [] { "run" }); + + _console.ConsoleCancelKey(); + + var exitCode = await program.OrTimeout(); + + Assert.Contains("Shutdown requested. Press Ctrl+C again to force exit.", stdout.ToString()); + Assert.Equal(0, exitCode); + } + public void Dispose() + { + _tempDir.Dispose(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/TaskExtensions.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TaskExtensions.cs similarity index 90% rename from test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/TaskExtensions.cs rename to test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TaskExtensions.cs index b93d843943..daaa77918e 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.FunctionalTests/TaskExtensions.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TaskExtensions.cs @@ -1,11 +1,9 @@ // 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.Runtime.CompilerServices; -using System.Threading.Tasks; -namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests +namespace System.Threading.Tasks { public static class TaskExtensions { diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryCSharpProject.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryCSharpProject.cs index f73ba47f5b..53ee3d38e4 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryCSharpProject.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryCSharpProject.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -namespace Microsoft.DotNetWatcher.Tools.Tests +namespace Microsoft.DotNet.Watcher.Tools.Tests { public class TemporaryCSharpProject { diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryDirectory.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryDirectory.cs index 3ec8d6bbee..26f36ec5f0 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryDirectory.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TemporaryDirectory.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.IO; -namespace Microsoft.DotNetWatcher.Tools.Tests +namespace Microsoft.DotNet.Watcher.Tools.Tests { public class TemporaryDirectory : IDisposable { diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TestProjectGraph.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TestProjectGraph.cs index 730f82f0db..a8e615d3c9 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TestProjectGraph.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/TestProjectGraph.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace Microsoft.DotNetWatcher.Tools.Tests +namespace Microsoft.DotNet.Watcher.Tools.Tests { public class TestProjectGraph { diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/XunitLogger.cs b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/XunitLogger.cs index 640a4a0664..0e48fde773 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/XunitLogger.cs +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/Utilities/XunitLogger.cs @@ -5,7 +5,7 @@ using System; using Microsoft.Extensions.Logging; using Xunit.Abstractions; -namespace Microsoft.DotNetWatcher.Tools.Tests +namespace Microsoft.DotNet.Watcher.Tools.Tests { internal class XunitLogger : ILogger { diff --git a/test/Microsoft.DotNet.Watcher.Tools.Tests/project.json b/test/Microsoft.DotNet.Watcher.Tools.Tests/project.json index b40ce98d3a..1916498359 100644 --- a/test/Microsoft.DotNet.Watcher.Tools.Tests/project.json +++ b/test/Microsoft.DotNet.Watcher.Tools.Tests/project.json @@ -2,7 +2,8 @@ "buildOptions": { "warningsAsErrors": true, "keyFile": "../../tools/Key.snk", - "debugType": "portable" + "debugType": "portable", + "compile": "../Shared/**/*.cs" }, "dependencies": { "dotnet-test-xunit": "2.2.0-preview2-build1029", diff --git a/test/Microsoft.Extensions.SecretManager.Tools.Tests/SecretManagerTests.cs b/test/Microsoft.Extensions.SecretManager.Tools.Tests/SecretManagerTests.cs index 9420fc2110..fba4b6e25b 100644 --- a/test/Microsoft.Extensions.SecretManager.Tools.Tests/SecretManagerTests.cs +++ b/test/Microsoft.Extensions.SecretManager.Tools.Tests/SecretManagerTests.cs @@ -9,6 +9,7 @@ using Microsoft.DotNet.Cli.Utils; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.Configuration.UserSecrets.Tests; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Tools.Internal; using Xunit; using Xunit.Abstractions; @@ -16,19 +17,20 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests { public class SecretManagerTests : IClassFixture { - private TestLogger _logger; + private readonly TestLogger _logger; + private readonly TestConsole _console; private readonly UserSecretsTestFixture _fixture; public SecretManagerTests(UserSecretsTestFixture fixture, ITestOutputHelper output) { _fixture = fixture; _logger = new TestLogger(output); - + _console = new TestConsole(output); } private Program CreateProgram() { - return new Program(new TestConsole(), Directory.GetCurrentDirectory()) + return new Program(_console, Directory.GetCurrentDirectory()) { Logger = _logger }; @@ -72,7 +74,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests var projectPath = _fixture.GetTempSecretProject(); var cwd = Path.Combine(projectPath, "nested1"); Directory.CreateDirectory(cwd); - var secretManager = new Program(new TestConsole(), cwd) { Logger = _logger, CommandOutputProvider = _logger.CommandOutputProvider }; + var secretManager = new Program(_console, cwd) { Logger = _logger, CommandOutputProvider = _logger.CommandOutputProvider }; secretManager.CommandOutputProvider.LogLevel = LogLevel.Debug; secretManager.RunInternal("list", "-p", ".." + Path.DirectorySeparatorChar, "--verbose"); @@ -97,7 +99,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests var dir = fromCurrentDirectory ? projectPath : Path.GetTempPath(); - var secretManager = new Program(new TestConsole(), dir) { Logger = _logger }; + var secretManager = new Program(_console, dir) { Logger = _logger }; foreach (var secret in secrets) { @@ -237,16 +239,13 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests public void List_Json() { var output = new StringBuilder(); - var testConsole = new TestConsole - { - Out = new StringWriter(output) - }; + _console.Out = new StringWriter(output); string id; var projectPath = _fixture.GetTempSecretProject(out id); var secretsFile = PathHelper.GetSecretsPathFromSecretsId(id); Directory.CreateDirectory(Path.GetDirectoryName(secretsFile)); File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abcd郩˙î""} }", Encoding.UTF8); - var secretManager = new Program(testConsole, Path.GetDirectoryName(projectPath)) { Logger = _logger }; + var secretManager = new Program(_console, Path.GetDirectoryName(projectPath)) { Logger = _logger }; secretManager.RunInternal("list", "--id", id, "--json"); var stdout = output.ToString(); Assert.Contains("//BEGIN", stdout); @@ -297,7 +296,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests ? projectPath : Path.GetTempPath(); - var secretManager = new Program(new TestConsole(), dir) { Logger = _logger }; + var secretManager = new Program(_console, dir) { Logger = _logger }; var secrets = new KeyValuePair[] { diff --git a/test/Microsoft.Extensions.SecretManager.Tools.Tests/SetCommandTest.cs b/test/Microsoft.Extensions.SecretManager.Tools.Tests/SetCommandTest.cs index 5420aa0ff4..90952ff5bd 100644 --- a/test/Microsoft.Extensions.SecretManager.Tools.Tests/SetCommandTest.cs +++ b/test/Microsoft.Extensions.SecretManager.Tools.Tests/SetCommandTest.cs @@ -1,17 +1,27 @@ // 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.IO; using System.Collections.Generic; using Microsoft.Extensions.SecretManager.Tools.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Tools.Internal; using Xunit; -using System; +using Xunit.Abstractions; namespace Microsoft.Extensions.SecretManager.Tools.Tests { + public class SetCommandTest { + private readonly ITestOutputHelper _output; + + public SetCommandTest(ITestOutputHelper output) + { + _output = output; + } + [Fact] public void SetsFromPipedInput() { @@ -21,7 +31,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests ""Key2"": 1234, ""Key3"": false }"; - var testConsole = new TestConsole + var testConsole = new TestConsole(_output) { IsInputRedirected = true, In = new StringReader(input) @@ -48,7 +58,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests ""array"": [ 1, 2 ] }"; - var testConsole = new TestConsole + var testConsole = new TestConsole(_output) { IsInputRedirected = true, In = new StringReader(input) @@ -68,7 +78,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests [Fact] public void OnlyPipesInIfNoArgs() { - var testConsole = new TestConsole + var testConsole = new TestConsole(_output) { IsInputRedirected = true, In = new StringReader("") diff --git a/test/Microsoft.Extensions.SecretManager.Tools.Tests/TestConsole.cs b/test/Microsoft.Extensions.SecretManager.Tools.Tests/TestConsole.cs deleted file mode 100644 index 8370ecf065..0000000000 --- a/test/Microsoft.Extensions.SecretManager.Tools.Tests/TestConsole.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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.IO; -using Microsoft.Extensions.SecretManager.Tools.Internal; - -namespace Microsoft.Extensions.SecretManager.Tools.Tests -{ - public class TestConsole : IConsole - { - public TextWriter Error { get; set; } = Console.Error; - public TextReader In { get; set; } = Console.In; - public TextWriter Out { get; set; } = Console.Out; - public bool IsInputRedirected { get; set; } = false; - } -} diff --git a/test/Microsoft.Extensions.SecretManager.Tools.Tests/project.json b/test/Microsoft.Extensions.SecretManager.Tools.Tests/project.json index b93fb60245..d2d23cb1fc 100644 --- a/test/Microsoft.Extensions.SecretManager.Tools.Tests/project.json +++ b/test/Microsoft.Extensions.SecretManager.Tools.Tests/project.json @@ -1,7 +1,8 @@ { "buildOptions": { "warningsAsErrors": true, - "keyFile": "../../tools/Key.snk" + "keyFile": "../../tools/Key.snk", + "compile": "../Shared/**/*.cs" }, "dependencies": { "dotnet-test-xunit": "2.2.0-preview2-build1029", diff --git a/test/Shared/TestConsole.cs b/test/Shared/TestConsole.cs new file mode 100644 index 0000000000..415cc60499 --- /dev/null +++ b/test/Shared/TestConsole.cs @@ -0,0 +1,69 @@ +// 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.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Tools.Internal +{ + public class TestConsole : IConsole + { + public TestConsole(ITestOutputHelper output) + { + var writer = new TestOutputWriter(output); + Error = writer; + Out = writer; + } + + public event ConsoleCancelEventHandler CancelKeyPress; + + public TextWriter Error { get; set; } + public TextWriter Out { get; set; } + public TextReader In { get; set; } = new StringReader(string.Empty); + public bool IsInputRedirected { get; set; } = false; + + public ConsoleCancelEventArgs ConsoleCancelKey() + { + var ctor = typeof(ConsoleCancelEventArgs) + .GetTypeInfo() + .DeclaredConstructors + .Single(c => c.GetParameters().First().ParameterType == typeof(ConsoleSpecialKey)); + var args = (ConsoleCancelEventArgs)ctor.Invoke(new object[] { ConsoleSpecialKey.ControlC }); + CancelKeyPress?.Invoke(this, args); + return args; + } + + private class TestOutputWriter : TextWriter + { + private readonly ITestOutputHelper _output; + private readonly StringBuilder _sb = new StringBuilder(); + + public TestOutputWriter(ITestOutputHelper output) + { + _output = output; + } + + public override Encoding Encoding => Encoding.Unicode; + + public override void Write(char value) + { + if (value == '\r' || value == '\n') + { + if (_sb.Length > 0) + { + _output.WriteLine(_sb.ToString()); + _sb.Clear(); + } + } + else + { + _sb.Append(value); + } + } + } + } +}