Abstract the console

This commit is contained in:
Nate McMaster 2016-11-16 21:35:21 -08:00
parent 587376200b
commit 40d4670463
No known key found for this signature in database
GPG Key ID: BD729980AA6A21BD
36 changed files with 235 additions and 155 deletions

View File

@ -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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.Watcher.Tools; using Microsoft.DotNet.Watcher.Tools;
using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.CommandLineUtils; using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher namespace Microsoft.DotNet.Watcher
{ {
@ -17,7 +16,7 @@ namespace Microsoft.DotNet.Watcher
public bool IsQuiet { get; private set; } public bool IsQuiet { get; private set; }
public bool IsVerbose { get; private set; } public bool IsVerbose { get; private set; }
public IList<string> RemainingArguments { get; private set; } public IList<string> 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)); Ensure.NotNull(args, nameof(args));
@ -25,8 +24,8 @@ namespace Microsoft.DotNet.Watcher
{ {
Name = "dotnet watch", Name = "dotnet watch",
FullName = "Microsoft DotNet File Watcher", FullName = "Microsoft DotNet File Watcher",
Out = stdout, Out = console.Out,
Error = stderr, Error = console.Error,
AllowArgumentSeparator = true, AllowArgumentSeparator = true,
ExtendedHelpText = @" ExtendedHelpText = @"
Environment variables: Environment variables:
@ -80,7 +79,7 @@ Examples:
if (optQuiet.HasValue() && optVerbose.HasValue()) if (optQuiet.HasValue() && optVerbose.HasValue())
{ {
stderr.WriteLine(Resources.Error_QuietAndVerboseSpecified.Bold().Red()); console.Error.WriteLine(Resources.Error_QuietAndVerboseSpecified.Bold().Red());
return null; return null;
} }

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher.Internal namespace Microsoft.DotNet.Watcher.Internal
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher.Internal namespace Microsoft.DotNet.Watcher.Internal
{ {

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher.Internal namespace Microsoft.DotNet.Watcher.Internal
{ {

View File

@ -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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.DotNet.Watcher.Tools; using Microsoft.DotNet.Watcher.Tools;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher.Internal namespace Microsoft.DotNet.Watcher.Internal
{ {

View File

@ -16,34 +16,36 @@ namespace Microsoft.DotNet.Watcher
public class Program public class Program
{ {
private const string LoggerName = "DotNetWatcher"; private const string LoggerName = "DotNetWatcher";
private readonly CancellationToken _cancellationToken; private readonly IConsole _console;
private readonly TextWriter _stdout;
private readonly TextWriter _stderr;
private readonly string _workingDir; 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(console, nameof(console));
Ensure.NotNull(consoleError, nameof(consoleError));
Ensure.NotNullOrEmpty(workingDir, nameof(workingDir)); Ensure.NotNullOrEmpty(workingDir, nameof(workingDir));
_cancellationToken = cancellationToken; _console = console;
_stdout = consoleOutput;
_stderr = consoleError;
_workingDir = workingDir; _workingDir = workingDir;
} }
public static int Main(string[] args) public static int Main(string[] args)
{ {
HandleDebugSwitch(ref args); HandleDebugSwitch(ref args);
return new Program(PhysicalConsole.Singleton, Directory.GetCurrentDirectory())
.RunAsync(args)
.GetAwaiter()
.GetResult();
}
public async Task<int> RunAsync(string[] args)
{
using (CancellationTokenSource ctrlCTokenSource = new CancellationTokenSource()) using (CancellationTokenSource ctrlCTokenSource = new CancellationTokenSource())
{ {
Console.CancelKeyPress += (sender, ev) => _console.CancelKeyPress += (sender, ev) =>
{ {
if (!ctrlCTokenSource.IsCancellationRequested) 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; ev.Cancel = true;
} }
else else
@ -55,10 +57,7 @@ namespace Microsoft.DotNet.Watcher
try try
{ {
return new Program(Console.Out, Console.Error, Directory.GetCurrentDirectory(), ctrlCTokenSource.Token) return await MainInternalAsync(args, ctrlCTokenSource.Token);
.MainInternalAsync(args)
.GetAwaiter()
.GetResult();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -68,16 +67,16 @@ namespace Microsoft.DotNet.Watcher
return 0; return 0;
} }
Console.Error.WriteLine(ex.ToString()); _console.Error.WriteLine(ex.ToString());
Console.Error.WriteLine($"[{LoggerName}] An unexpected error occurred".Bold().Red()); _console.Error.WriteLine($"[{LoggerName}] An unexpected error occurred".Bold().Red());
return 1; return 1;
} }
} }
} }
private async Task<int> MainInternalAsync(string[] args) private async Task<int> MainInternalAsync(string[] args, CancellationToken cancellationToken)
{ {
var options = CommandLineOptions.Parse(args, _stdout, _stdout); var options = CommandLineOptions.Parse(args, _console);
if (options == null) if (options == null)
{ {
// invalid args syntax // invalid args syntax
@ -105,7 +104,7 @@ namespace Microsoft.DotNet.Watcher
} }
catch (FileNotFoundException ex) catch (FileNotFoundException ex)
{ {
_stderr.WriteLine(ex.Message.Bold().Red()); _console.Error.WriteLine(ex.Message.Bold().Red());
return 1; return 1;
} }
@ -119,7 +118,7 @@ namespace Microsoft.DotNet.Watcher
}; };
await new DotNetWatcher(logger) await new DotNetWatcher(logger)
.WatchAsync(processInfo, fileSetFactory, _cancellationToken); .WatchAsync(processInfo, fileSetFactory, cancellationToken);
return 0; return 0;
} }

View File

@ -74,22 +74,6 @@ namespace Microsoft.DotNet.Watcher.Tools
return GetString("Error_QuietAndVerboseSpecified"); return GetString("Error_QuietAndVerboseSpecified");
} }
/// <summary>
/// Value cannot be null or an empty string.
/// </summary>
internal static string Error_StringNullOrEmpty
{
get { return GetString("Error_StringNullOrEmpty"); }
}
/// <summary>
/// Value cannot be null or an empty string.
/// </summary>
internal static string FormatError_StringNullOrEmpty()
{
return GetString("Error_StringNullOrEmpty");
}
private static string GetString(string name, params string[] formatterNames) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -129,7 +129,4 @@
<data name="Error_QuietAndVerboseSpecified" xml:space="preserve"> <data name="Error_QuietAndVerboseSpecified" xml:space="preserve">
<value>Cannot specify both '--quiet' and '--verbose' options.</value> <value>Cannot specify both '--quiet' and '--verbose' options.</value>
</data> </data>
<data name="Error_StringNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or an empty string.</value>
</data>
</root> </root>

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Logging;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.Extensions.SecretManager.Tools.Internal namespace Microsoft.Extensions.SecretManager.Tools.Internal
{ {

View File

@ -4,6 +4,7 @@
using System.Reflection; using System.Reflection;
using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.CommandLineUtils; using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.Extensions.SecretManager.Tools.Internal namespace Microsoft.Extensions.SecretManager.Tools.Internal
{ {

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.Extensions.SecretManager.Tools.Internal namespace Microsoft.Extensions.SecretManager.Tools.Internal
{ {
@ -14,10 +15,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
public MsBuildProjectFinder(string directory) public MsBuildProjectFinder(string directory)
{ {
if (string.IsNullOrEmpty(directory)) Ensure.NotNullOrEmpty(directory, nameof(directory));
{
throw new ArgumentException(Resources.Common_StringNullOrEmpty, nameof(directory));
}
_directory = directory; _directory = directory;
} }

View File

@ -9,6 +9,7 @@ using System.Text;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Tools.Internal;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.SecretManager.Tools.Internal namespace Microsoft.Extensions.SecretManager.Tools.Internal
@ -20,10 +21,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal
public SecretsStore(string userSecretsId, ILogger logger) public SecretsStore(string userSecretsId, ILogger logger)
{ {
if (userSecretsId == null) Ensure.NotNull(userSecretsId, nameof(userSecretsId));
{
throw new ArgumentNullException(nameof(userSecretsId));
}
_secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId); _secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId);

View File

@ -6,6 +6,7 @@ using System.IO;
using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.SecretManager.Tools.Internal; using Microsoft.Extensions.SecretManager.Tools.Internal;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.Extensions.SecretManager.Tools namespace Microsoft.Extensions.SecretManager.Tools
{ {
@ -39,29 +40,13 @@ namespace Microsoft.Extensions.SecretManager.Tools
public ILogger Logger public ILogger Logger
{ {
get { return _logger; } get { return _logger; }
set set { _logger = Ensure.NotNull(value, nameof(value)); }
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_logger = value;
}
} }
public CommandOutputProvider CommandOutputProvider public CommandOutputProvider CommandOutputProvider
{ {
get { return _loggerProvider; } get { return _loggerProvider; }
set set { _loggerProvider = Ensure.NotNull(value, nameof(value)); }
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_loggerProvider = value;
}
} }
public bool TryRun(string[] args, out int returnCode) public bool TryRun(string[] args, out int returnCode)

View File

@ -10,22 +10,6 @@ namespace Microsoft.Extensions.SecretManager.Tools
private static readonly ResourceManager _resourceManager private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.Extensions.SecretManager.Tools.Resources", typeof(Resources).GetTypeInfo().Assembly); = new ResourceManager("Microsoft.Extensions.SecretManager.Tools.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// Value cannot be null or an empty string.
/// </summary>
internal static string Common_StringNullOrEmpty
{
get { return GetString("Common_StringNullOrEmpty"); }
}
/// <summary>
/// Value cannot be null or an empty string.
/// </summary>
internal static string FormatCommon_StringNullOrEmpty()
{
return GetString("Common_StringNullOrEmpty");
}
/// <summary> /// <summary>
/// Command failed : {message} /// Command failed : {message}
/// </summary> /// </summary>

View File

@ -117,9 +117,6 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Common_StringNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or an empty string.</value>
</data>
<data name="Error_Command_Failed" xml:space="preserve"> <data name="Error_Command_Failed" xml:space="preserve">
<value>Command failed : {message}</value> <value>Command failed : {message}</value>
</data> </data>

View File

@ -2,9 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Microsoft.DotNet.Watcher.Tools;
namespace Microsoft.DotNet.Watcher.Internal namespace Microsoft.Extensions.Tools.Internal
{ {
internal static class Ensure internal static class Ensure
{ {
@ -22,7 +21,7 @@ namespace Microsoft.DotNet.Watcher.Internal
{ {
if (string.IsNullOrEmpty(obj)) 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; return obj;
} }

View File

@ -1,12 +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. // 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.IO;
namespace Microsoft.Extensions.SecretManager.Tools.Internal namespace Microsoft.Extensions.Tools.Internal
{ {
public interface IConsole public interface IConsole
{ {
event ConsoleCancelEventHandler CancelKeyPress;
TextWriter Out { get; } TextWriter Out { get; }
TextWriter Error { get; } TextWriter Error { get; }
TextReader In { get; } TextReader In { get; }

View File

@ -4,13 +4,21 @@
using System; using System;
using System.IO; using System.IO;
namespace Microsoft.Extensions.SecretManager.Tools.Internal namespace Microsoft.Extensions.Tools.Internal
{ {
public class PhysicalConsole : IConsole public class PhysicalConsole : IConsole
{ {
private PhysicalConsole() { } private PhysicalConsole()
{
Console.CancelKeyPress += (o, e) =>
{
CancelKeyPress?.Invoke(o, e);
};
}
public static IConsole Singleton { get; } = new PhysicalConsole(); public static IConsole Singleton { get; } = new PhysicalConsole();
public event ConsoleCancelEventHandler CancelKeyPress;
public TextWriter Error => Console.Error; public TextWriter Error => Console.Error;
public TextReader In => Console.In; public TextReader In => Console.In;
public TextWriter Out => Console.Out; public TextWriter Out => Console.Out;

View File

@ -15,6 +15,7 @@
"Microsoft.DotNet.InternalAbstractions": "1.0.0", "Microsoft.DotNet.InternalAbstractions": "1.0.0",
"Microsoft.AspNetCore.Testing": "1.0.0", "Microsoft.AspNetCore.Testing": "1.0.0",
"Microsoft.DotNet.Watcher.Tools": "1.0.0-*", "Microsoft.DotNet.Watcher.Tools": "1.0.0-*",
"Microsoft.DotNet.Watcher.Tools.Tests": "1.0.0-*",
"Microsoft.Extensions.Process.Sources": { "Microsoft.Extensions.Process.Sources": {
"type": "build", "type": "build",
"version": "1.0.0" "version": "1.0.0"

View File

@ -1,3 +1,5 @@
set -e
dotnet build dotnet build
../../.build/dotnet/dotnet exec \ ../../.build/dotnet/dotnet exec \

View File

@ -7,7 +7,7 @@ using System.IO;
using System.Linq; using System.Linq;
using Xunit; using Xunit;
namespace Microsoft.DotNetWatcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
public static class AssertEx public static class AssertEx
{ {

View File

@ -4,12 +4,27 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Microsoft.Extensions.Tools.Internal;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Microsoft.DotNet.Watcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
public class CommandLineOptionsTests 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] [Theory]
[InlineData(new object[] { new[] { "-h" } })] [InlineData(new object[] { new[] { "-h" } })]
[InlineData(new object[] { new[] { "-?" } })] [InlineData(new object[] { new[] { "-?" } })]
@ -19,12 +34,10 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests
[InlineData(new object[] { new string[0] })] [InlineData(new object[] { new string[0] })]
public void HelpArgs(string[] args) public void HelpArgs(string[] args)
{ {
var stdout = new StringBuilder(); var options = CommandLineOptions.Parse(args, _console);
var options = CommandLineOptions.Parse(args, new StringWriter(stdout), new StringWriter());
Assert.True(options.IsHelp); Assert.True(options.IsHelp);
Assert.Contains("Usage: dotnet watch ", stdout.ToString()); Assert.Contains("Usage: dotnet watch ", _stdout.ToString());
} }
[Theory] [Theory]
@ -34,22 +47,18 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests
[InlineData(new[] { "--unrecognized-arg" }, new[] { "--unrecognized-arg" })] [InlineData(new[] { "--unrecognized-arg" }, new[] { "--unrecognized-arg" })]
public void ParsesRemainingArgs(string[] args, string[] expected) public void ParsesRemainingArgs(string[] args, string[] expected)
{ {
var stdout = new StringBuilder(); var options = CommandLineOptions.Parse(args, _console);
var options = CommandLineOptions.Parse(args, new StringWriter(stdout), new StringWriter());
Assert.Equal(expected, options.RemainingArguments.ToArray()); Assert.Equal(expected, options.RemainingArguments.ToArray());
Assert.False(options.IsHelp); Assert.False(options.IsHelp);
Assert.Empty(stdout.ToString()); Assert.Empty(_stdout.ToString());
} }
[Fact] [Fact]
public void CannotHaveQuietAndVerbose() public void CannotHaveQuietAndVerbose()
{ {
var sb = new StringBuilder(); Assert.Null(CommandLineOptions.Parse(new[] { "--quiet", "--verbose" }, _console));
var stderr = new StringWriter(sb); Assert.Contains(Resources.Error_QuietAndVerboseSpecified, _stderr.ToString());
Assert.Null(CommandLineOptions.Parse(new[] { "--quiet", "--verbose" }, new StringWriter(), stderr));
Assert.Contains(Resources.Error_QuietAndVerboseSpecified, sb.ToString());
} }
} }
} }

View File

@ -6,13 +6,12 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.DotNet.Watcher;
using Microsoft.DotNet.Watcher.Internal; using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace Microsoft.DotNetWatcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
using ItemSpec = TemporaryCSharpProject.ItemSpec; using ItemSpec = TemporaryCSharpProject.ItemSpec;

View File

@ -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();
}
}
}

View File

@ -1,11 +1,9 @@
// 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. // 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.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests namespace System.Threading.Tasks
{ {
public static class TaskExtensions public static class TaskExtensions
{ {

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
namespace Microsoft.DotNetWatcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
public class TemporaryCSharpProject public class TemporaryCSharpProject
{ {

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace Microsoft.DotNetWatcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
public class TemporaryDirectory : IDisposable public class TemporaryDirectory : IDisposable
{ {

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.DotNetWatcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
public class TestProjectGraph public class TestProjectGraph
{ {

View File

@ -5,7 +5,7 @@ using System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace Microsoft.DotNetWatcher.Tools.Tests namespace Microsoft.DotNet.Watcher.Tools.Tests
{ {
internal class XunitLogger : ILogger internal class XunitLogger : ILogger
{ {

View File

@ -2,7 +2,8 @@
"buildOptions": { "buildOptions": {
"warningsAsErrors": true, "warningsAsErrors": true,
"keyFile": "../../tools/Key.snk", "keyFile": "../../tools/Key.snk",
"debugType": "portable" "debugType": "portable",
"compile": "../Shared/**/*.cs"
}, },
"dependencies": { "dependencies": {
"dotnet-test-xunit": "2.2.0-preview2-build1029", "dotnet-test-xunit": "2.2.0-preview2-build1029",

View File

@ -9,6 +9,7 @@ using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Configuration.UserSecrets.Tests; using Microsoft.Extensions.Configuration.UserSecrets.Tests;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Tools.Internal;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -16,19 +17,20 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
{ {
public class SecretManagerTests : IClassFixture<UserSecretsTestFixture> public class SecretManagerTests : IClassFixture<UserSecretsTestFixture>
{ {
private TestLogger _logger; private readonly TestLogger _logger;
private readonly TestConsole _console;
private readonly UserSecretsTestFixture _fixture; private readonly UserSecretsTestFixture _fixture;
public SecretManagerTests(UserSecretsTestFixture fixture, ITestOutputHelper output) public SecretManagerTests(UserSecretsTestFixture fixture, ITestOutputHelper output)
{ {
_fixture = fixture; _fixture = fixture;
_logger = new TestLogger(output); _logger = new TestLogger(output);
_console = new TestConsole(output);
} }
private Program CreateProgram() private Program CreateProgram()
{ {
return new Program(new TestConsole(), Directory.GetCurrentDirectory()) return new Program(_console, Directory.GetCurrentDirectory())
{ {
Logger = _logger Logger = _logger
}; };
@ -72,7 +74,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
var projectPath = _fixture.GetTempSecretProject(); var projectPath = _fixture.GetTempSecretProject();
var cwd = Path.Combine(projectPath, "nested1"); var cwd = Path.Combine(projectPath, "nested1");
Directory.CreateDirectory(cwd); 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.CommandOutputProvider.LogLevel = LogLevel.Debug;
secretManager.RunInternal("list", "-p", ".." + Path.DirectorySeparatorChar, "--verbose"); secretManager.RunInternal("list", "-p", ".." + Path.DirectorySeparatorChar, "--verbose");
@ -97,7 +99,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
var dir = fromCurrentDirectory var dir = fromCurrentDirectory
? projectPath ? projectPath
: Path.GetTempPath(); : Path.GetTempPath();
var secretManager = new Program(new TestConsole(), dir) { Logger = _logger }; var secretManager = new Program(_console, dir) { Logger = _logger };
foreach (var secret in secrets) foreach (var secret in secrets)
{ {
@ -237,16 +239,13 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
public void List_Json() public void List_Json()
{ {
var output = new StringBuilder(); var output = new StringBuilder();
var testConsole = new TestConsole _console.Out = new StringWriter(output);
{
Out = new StringWriter(output)
};
string id; string id;
var projectPath = _fixture.GetTempSecretProject(out id); var projectPath = _fixture.GetTempSecretProject(out id);
var secretsFile = PathHelper.GetSecretsPathFromSecretsId(id); var secretsFile = PathHelper.GetSecretsPathFromSecretsId(id);
Directory.CreateDirectory(Path.GetDirectoryName(secretsFile)); Directory.CreateDirectory(Path.GetDirectoryName(secretsFile));
File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abcd郩˙î""} }", Encoding.UTF8); 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"); secretManager.RunInternal("list", "--id", id, "--json");
var stdout = output.ToString(); var stdout = output.ToString();
Assert.Contains("//BEGIN", stdout); Assert.Contains("//BEGIN", stdout);
@ -297,7 +296,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
? projectPath ? projectPath
: Path.GetTempPath(); : Path.GetTempPath();
var secretManager = new Program(new TestConsole(), dir) { Logger = _logger }; var secretManager = new Program(_console, dir) { Logger = _logger };
var secrets = new KeyValuePair<string, string>[] var secrets = new KeyValuePair<string, string>[]
{ {

View File

@ -1,17 +1,27 @@
// 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. // 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.IO;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Extensions.SecretManager.Tools.Internal; using Microsoft.Extensions.SecretManager.Tools.Internal;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Tools.Internal;
using Xunit; using Xunit;
using System; using Xunit.Abstractions;
namespace Microsoft.Extensions.SecretManager.Tools.Tests namespace Microsoft.Extensions.SecretManager.Tools.Tests
{ {
public class SetCommandTest public class SetCommandTest
{ {
private readonly ITestOutputHelper _output;
public SetCommandTest(ITestOutputHelper output)
{
_output = output;
}
[Fact] [Fact]
public void SetsFromPipedInput() public void SetsFromPipedInput()
{ {
@ -21,7 +31,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
""Key2"": 1234, ""Key2"": 1234,
""Key3"": false ""Key3"": false
}"; }";
var testConsole = new TestConsole var testConsole = new TestConsole(_output)
{ {
IsInputRedirected = true, IsInputRedirected = true,
In = new StringReader(input) In = new StringReader(input)
@ -48,7 +58,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
""array"": [ 1, 2 ] ""array"": [ 1, 2 ]
}"; }";
var testConsole = new TestConsole var testConsole = new TestConsole(_output)
{ {
IsInputRedirected = true, IsInputRedirected = true,
In = new StringReader(input) In = new StringReader(input)
@ -68,7 +78,7 @@ namespace Microsoft.Extensions.SecretManager.Tools.Tests
[Fact] [Fact]
public void OnlyPipesInIfNoArgs() public void OnlyPipesInIfNoArgs()
{ {
var testConsole = new TestConsole var testConsole = new TestConsole(_output)
{ {
IsInputRedirected = true, IsInputRedirected = true,
In = new StringReader("") In = new StringReader("")

View File

@ -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;
}
}

View File

@ -1,7 +1,8 @@
{ {
"buildOptions": { "buildOptions": {
"warningsAsErrors": true, "warningsAsErrors": true,
"keyFile": "../../tools/Key.snk" "keyFile": "../../tools/Key.snk",
"compile": "../Shared/**/*.cs"
}, },
"dependencies": { "dependencies": {
"dotnet-test-xunit": "2.2.0-preview2-build1029", "dotnet-test-xunit": "2.2.0-preview2-build1029",

View File

@ -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);
}
}
}
}
}