Fixing the BuiltInCommandTests to not mutate global shared state.

The BuiltInCommandTests sets the current Console.Out and Console.Error, which causes the test to fail if some other test is running and writes to the console at the same time.

Fix #2768
This commit is contained in:
Eric Erhardt 2016-05-02 17:46:48 -05:00
parent 9a85205781
commit 16b996f25b
5 changed files with 121 additions and 17 deletions

View file

@ -18,6 +18,7 @@ namespace Microsoft.DotNet.Cli.Utils
{ {
private readonly IEnumerable<string> _commandArgs; private readonly IEnumerable<string> _commandArgs;
private readonly Func<string[], int> _builtInCommand; private readonly Func<string[], int> _builtInCommand;
private readonly IBuiltInCommandEnvironment _environment;
private readonly StreamForwarder _stdOut; private readonly StreamForwarder _stdOut;
private readonly StreamForwarder _stdErr; private readonly StreamForwarder _stdErr;
private string _workingDirectory; private string _workingDirectory;
@ -26,10 +27,16 @@ namespace Microsoft.DotNet.Cli.Utils
public string CommandArgs => string.Join(" ", _commandArgs); public string CommandArgs => string.Join(" ", _commandArgs);
public BuiltInCommand(string commandName, IEnumerable<string> commandArgs, Func<string[], int> builtInCommand) public BuiltInCommand(string commandName, IEnumerable<string> commandArgs, Func<string[], int> builtInCommand)
: this(commandName, commandArgs, builtInCommand, new BuiltInCommandEnvironment())
{
}
internal BuiltInCommand(string commandName, IEnumerable<string> commandArgs, Func<string[], int> builtInCommand, IBuiltInCommandEnvironment environment)
{ {
CommandName = commandName; CommandName = commandName;
_commandArgs = commandArgs; _commandArgs = commandArgs;
_builtInCommand = builtInCommand; _builtInCommand = builtInCommand;
_environment = environment;
_stdOut = new StreamForwarder(); _stdOut = new StreamForwarder();
_stdErr = new StreamForwarder(); _stdErr = new StreamForwarder();
@ -37,9 +44,9 @@ namespace Microsoft.DotNet.Cli.Utils
public CommandResult Execute() public CommandResult Execute()
{ {
TextWriter originalConsoleOut = Console.Out; TextWriter originalConsoleOut = _environment.GetConsoleOut();
TextWriter originalConsoleError = Console.Error; TextWriter originalConsoleError = _environment.GetConsoleError();
string originalWorkingDirectory = Directory.GetCurrentDirectory(); string originalWorkingDirectory = _environment.GetWorkingDirectory();
try try
{ {
@ -48,15 +55,15 @@ namespace Microsoft.DotNet.Cli.Utils
using (BlockingMemoryStream outStream = new BlockingMemoryStream()) using (BlockingMemoryStream outStream = new BlockingMemoryStream())
using (BlockingMemoryStream errorStream = new BlockingMemoryStream()) using (BlockingMemoryStream errorStream = new BlockingMemoryStream())
{ {
Console.SetOut(new StreamWriter(outStream) { AutoFlush = true }); _environment.SetConsoleOut(new StreamWriter(outStream) { AutoFlush = true });
Console.SetError(new StreamWriter(errorStream) { AutoFlush = true }); _environment.SetConsoleError(new StreamWriter(errorStream) { AutoFlush = true });
// Reset the Reporters to the new Console Out and Error. // Reset the Reporters to the new Console Out and Error.
Reporter.Reset(); Reporter.Reset();
if (!string.IsNullOrEmpty(_workingDirectory)) if (!string.IsNullOrEmpty(_workingDirectory))
{ {
Directory.SetCurrentDirectory(_workingDirectory); _environment.SetWorkingDirectory(_workingDirectory);
} }
var taskOut = _stdOut.BeginRead(new StreamReader(outStream)); var taskOut = _stdOut.BeginRead(new StreamReader(outStream));
@ -76,9 +83,9 @@ namespace Microsoft.DotNet.Cli.Utils
} }
finally finally
{ {
Console.SetOut(originalConsoleOut); _environment.SetConsoleOut(originalConsoleOut);
Console.SetError(originalConsoleError); _environment.SetConsoleError(originalConsoleError);
Directory.SetCurrentDirectory(originalWorkingDirectory); _environment.SetWorkingDirectory(originalWorkingDirectory);
Reporter.Reset(); Reporter.Reset();
} }
@ -115,6 +122,39 @@ namespace Microsoft.DotNet.Cli.Utils
return this; return this;
} }
private class BuiltInCommandEnvironment : IBuiltInCommandEnvironment
{
public TextWriter GetConsoleOut()
{
return Console.Out;
}
public void SetConsoleOut(TextWriter newOut)
{
Console.SetOut(newOut);
}
public TextWriter GetConsoleError()
{
return Console.Error;
}
public void SetConsoleError(TextWriter newError)
{
Console.SetError(newError);
}
public string GetWorkingDirectory()
{
return Directory.GetCurrentDirectory();
}
public void SetWorkingDirectory(string path)
{
Directory.SetCurrentDirectory(path);
}
}
public CommandResolutionStrategy ResolutionStrategy public CommandResolutionStrategy ResolutionStrategy
{ {
get get

View file

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.IO;
namespace Microsoft.DotNet.Cli.Utils
{
internal interface IBuiltInCommandEnvironment
{
TextWriter GetConsoleOut();
void SetConsoleOut(TextWriter newOut);
TextWriter GetConsoleError();
void SetConsoleError(TextWriter newError);
string GetWorkingDirectory();
void SetWorkingDirectory(string path);
}
}

View file

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.DotNet.Cli.Utils.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View file

@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using Microsoft.DotNet.Tools.Test.Utilities; using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit; using Xunit;
@ -35,16 +36,20 @@ namespace Microsoft.DotNet.Cli.Utils
[Fact] [Fact]
public void TestOnOutputLines() public void TestOnOutputLines()
{ {
int exitCode = 29; const int exitCode = 29;
TestBuiltInCommandEnvironment environment = new TestBuiltInCommandEnvironment();
Func<string[], int> testCommand = args => Func<string[], int> testCommand = args =>
{ {
Console.Out.Write("first"); TextWriter outWriter = environment.GetConsoleOut();
Console.Out.WriteLine("second"); outWriter.Write("first");
Console.Out.WriteLine("third"); outWriter.WriteLine("second");
outWriter.WriteLine("third");
Console.Error.WriteLine("fourth"); TextWriter errorWriter = environment.GetConsoleError();
Console.Error.WriteLine("fifth"); errorWriter.WriteLine("fourth");
errorWriter.WriteLine("fifth");
return exitCode; return exitCode;
}; };
@ -52,7 +57,7 @@ namespace Microsoft.DotNet.Cli.Utils
int onOutputLineCallCount = 0; int onOutputLineCallCount = 0;
int onErrorLineCallCount = 0; int onErrorLineCallCount = 0;
CommandResult result = new BuiltInCommand("fakeCommand", Enumerable.Empty<string>(), testCommand) CommandResult result = new BuiltInCommand("fakeCommand", Enumerable.Empty<string>(), testCommand, environment)
.OnOutputLine(line => .OnOutputLine(line =>
{ {
onOutputLineCallCount++; onOutputLineCallCount++;
@ -85,5 +90,41 @@ namespace Microsoft.DotNet.Cli.Utils
Assert.Equal(2, onOutputLineCallCount); Assert.Equal(2, onOutputLineCallCount);
Assert.Equal(2, onErrorLineCallCount); Assert.Equal(2, onErrorLineCallCount);
} }
private class TestBuiltInCommandEnvironment : IBuiltInCommandEnvironment
{
private TextWriter _consoleOut;
private TextWriter _consoleError;
public TextWriter GetConsoleOut()
{
return _consoleOut;
}
public void SetConsoleOut(TextWriter newOut)
{
_consoleOut = newOut;
}
public TextWriter GetConsoleError()
{
return _consoleError;
}
public void SetConsoleError(TextWriter newError)
{
_consoleError = newError;
}
public string GetWorkingDirectory()
{
return Directory.GetCurrentDirectory();
}
public void SetWorkingDirectory(string path)
{
// no-op
}
}
} }
} }

View file

@ -1,7 +1,8 @@
{ {
"version": "1.0.0-*", "version": "1.0.0-*",
"compilationOptions": { "compilationOptions": {
"emitEntryPoint": true "emitEntryPoint": true,
"keyFile": "../../tools/Key.snk"
}, },
"dependencies": { "dependencies": {
"Microsoft.NETCore.App": { "Microsoft.NETCore.App": {