Merge pull request #9099 from peterhuene/razor-server-discovery

Implement Razor server discovery by pid file.
This commit is contained in:
Peter Huene 2018-04-20 12:15:52 -07:00 committed by GitHub
commit e1b5272540
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1115 additions and 716 deletions

View file

@ -0,0 +1,22 @@
// 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;
namespace Microsoft.DotNet.BuildServer
{
internal class BuildServerException : Exception
{
public BuildServerException()
{
}
public BuildServerException(string message) : base(message)
{
}
public BuildServerException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View file

@ -0,0 +1,86 @@
// 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;
using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal class BuildServerProvider : IBuildServerProvider
{
private readonly IFileSystem _fileSystem;
private readonly IEnvironmentProvider _environmentProvider;
public BuildServerProvider(
IFileSystem fileSystem = null,
IEnvironmentProvider environmentProvider = null)
{
_fileSystem = fileSystem ?? FileSystemWrapper.Default;
_environmentProvider = environmentProvider ?? new EnvironmentProvider();
}
public IEnumerable<IBuildServer> EnumerateBuildServers(ServerEnumerationFlags flags = ServerEnumerationFlags.All)
{
if ((flags & ServerEnumerationFlags.MSBuild) == ServerEnumerationFlags.MSBuild)
{
// Yield a single MSBuild server (handles server discovery itself)
// TODO: use pid file enumeration when supported by the server (https://github.com/dotnet/cli/issues/9113)
yield return new MSBuildServer();
}
if ((flags & ServerEnumerationFlags.VBCSCompiler) == ServerEnumerationFlags.VBCSCompiler)
{
// Yield a single VB/C# compiler (handles server discovery itself)
// TODO: use pid file enumeration when supported by the server (https://github.com/dotnet/cli/issues/9112)
yield return new VBCSCompilerServer();
}
// TODO: remove or amend this check when the following issues are resolved:
// https://github.com/dotnet/cli/issues/9112
// https://github.com/dotnet/cli/issues/9113
if ((flags & ServerEnumerationFlags.Razor) != ServerEnumerationFlags.Razor)
{
yield break;
}
var directory = GetPidFileDirectory();
if (!_fileSystem.Directory.Exists(directory.Value))
{
yield break;
}
foreach (var path in _fileSystem.Directory.EnumerateFiles(directory.Value, "*"))
{
if ((flags & ServerEnumerationFlags.Razor) == ServerEnumerationFlags.Razor &&
Path.GetFileName(path).StartsWith(RazorPidFile.FilePrefix))
{
var file = RazorPidFile.Read(new FilePath(path), _fileSystem);
if (file != null)
{
yield return new RazorServer(file);
}
}
}
}
public DirectoryPath GetPidFileDirectory()
{
var directory = _environmentProvider.GetEnvironmentVariable("DOTNET_BUILD_PIDFILE_DIRECTORY");
if (!string.IsNullOrEmpty(directory))
{
return new DirectoryPath(directory);
}
return new DirectoryPath(
Path.Combine(
CliFolderPathCalculator.DotnetUserProfileFolderPath,
"pids",
"build"));
}
}
}

View file

@ -6,10 +6,12 @@ using System.Threading.Tasks;
namespace Microsoft.DotNet.BuildServer
{
internal interface IBuildServerManager
internal interface IBuildServer
{
string ServerName { get; }
int ProcessId { get; }
Task<Result> ShutdownServerAsync();
string Name { get; }
void Shutdown();
}
}

View file

@ -0,0 +1,23 @@
// 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;
using System.Collections.Generic;
namespace Microsoft.DotNet.BuildServer
{
[Flags]
internal enum ServerEnumerationFlags
{
None = 0,
MSBuild = 1,
VBCSCompiler = 2,
Razor = 4,
All = MSBuild | VBCSCompiler | Razor
}
internal interface IBuildServerProvider
{
IEnumerable<IBuildServer> EnumerateBuildServers(ServerEnumerationFlags flags = ServerEnumerationFlags.All);
}
}

View file

@ -1,14 +0,0 @@
// 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;
using System.Collections.Generic;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal interface IRazorAssemblyResolver
{
IEnumerable<FilePath> EnumerateRazorToolAssemblies();
}
}

View file

@ -126,7 +126,7 @@
<data name="RazorServer" xml:space="preserve">
<value>Razor build server</value>
</data>
<data name="NoRazorProjectFound" xml:space="preserve">
<value>a Razor project was not found in the current directory.</value>
<data name="ShutdownCommandFailed" xml:space="preserve">
<value>The shutdown command failed: {0}</value>
</data>
</root>

View file

@ -0,0 +1,21 @@
// 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;
using System.Threading.Tasks;
using Microsoft.Build.Execution;
namespace Microsoft.DotNet.BuildServer
{
internal class MSBuildServer : IBuildServer
{
public int ProcessId => 0; // Not yet used
public string Name => LocalizableStrings.MSBuildServer;
public void Shutdown()
{
BuildManager.DefaultBuildManager.ShutdownAllNodes();
}
}
}

View file

@ -1,29 +0,0 @@
// 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;
using System.Threading.Tasks;
using Microsoft.Build.Execution;
namespace Microsoft.DotNet.BuildServer
{
internal class MSBuildServerManager : IBuildServerManager
{
public string ServerName => LocalizableStrings.MSBuildServer;
public Task<Result> ShutdownServerAsync()
{
return Task.Run(() => {
try
{
BuildManager.DefaultBuildManager.ShutdownAllNodes();
return new Result(ResultKind.Success);
}
catch (Exception ex)
{
return new Result(ex);
}
});
}
}
}

View file

@ -1,52 +0,0 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Build.Execution;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal class RazorAssemblyResolver : IRazorAssemblyResolver
{
private readonly IDirectory _directory;
public RazorAssemblyResolver(IDirectory directory = null)
{
_directory = directory ?? FileSystemWrapper.Default.Directory;
}
public IEnumerable<FilePath> EnumerateRazorToolAssemblies()
{
HashSet<string> seen = new HashSet<string>();
var globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
// This property disables default item globbing to improve performance
// This should be safe because we are not evaluating items, only properties
{ Constants.EnableDefaultItems, "false" }
};
foreach (var projectFile in _directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.*proj"))
{
var project = new ProjectInstance(projectFile, globalProperties, null);
var path = project.GetPropertyValue("_RazorToolAssembly");
if (string.IsNullOrEmpty(path))
{
continue;
}
if (!seen.Add(path))
{
continue;
}
yield return new FilePath(path);
}
}
}
}

View file

@ -0,0 +1,65 @@
// 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;
using System.IO;
using System.Text;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal class RazorPidFile
{
public const string RazorServerType = "rzc";
public const string FilePrefix = "rzc-";
public RazorPidFile(FilePath path, int processId, FilePath serverPath, string pipeName)
{
Path = path;
ProcessId = processId;
ServerPath = serverPath;
PipeName = pipeName ?? throw new ArgumentNullException(pipeName);
}
public FilePath Path { get; }
public int ProcessId;
public FilePath ServerPath { get; }
public string PipeName { get; }
public static RazorPidFile Read(FilePath path, IFileSystem fileSystem = null)
{
fileSystem = fileSystem ?? FileSystemWrapper.Default;
using (var stream = fileSystem.File.OpenRead(path.Value))
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
if (!int.TryParse(reader.ReadLine(), out var processId))
{
return null;
}
if (reader.ReadLine() != RazorServerType)
{
return null;
}
var serverPath = reader.ReadLine();
if (string.IsNullOrEmpty(serverPath))
{
return null;
}
var pipeName = reader.ReadLine();
if (string.IsNullOrEmpty(pipeName))
{
return null;
}
return new RazorPidFile(path, processId, new FilePath(serverPath), pipeName);
}
}
}
}

View file

@ -0,0 +1,77 @@
// 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;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal class RazorServer : IBuildServer
{
private readonly ICommandFactory _commandFactory;
private readonly IFileSystem _fileSystem;
public RazorServer(
RazorPidFile pidFile,
ICommandFactory commandFactory = null,
IFileSystem fileSystem = null)
{
PidFile = pidFile ?? throw new ArgumentNullException(nameof(pidFile));
_commandFactory = commandFactory ?? new DotNetCommandFactory(alwaysRunOutOfProc: true);
_fileSystem = fileSystem ?? FileSystemWrapper.Default;
}
public int ProcessId => PidFile.ProcessId;
public string Name => LocalizableStrings.RazorServer;
public RazorPidFile PidFile { get; }
public void Shutdown()
{
var command = _commandFactory
.Create(
"exec",
new string[] {
PidFile.ServerPath.Value,
"shutdown",
"-w", // Wait for exit
"-p", // Pipe name
PidFile.PipeName
})
.CaptureStdOut()
.CaptureStdErr();
var result = command.Execute();
if (result.ExitCode != 0)
{
throw new BuildServerException(
string.Format(
LocalizableStrings.ShutdownCommandFailed,
result.StdErr));
}
// After a successful shutdown, ensure the pid file is deleted
// If the pid file was left behind due to a rude exit, this ensures we don't try to shut it down again
try
{
if (_fileSystem.File.Exists(PidFile.Path.Value))
{
_fileSystem.File.Delete(PidFile.Path.Value);
}
}
catch (UnauthorizedAccessException)
{
}
catch (IOException)
{
}
}
}
}

View file

@ -1,65 +0,0 @@
// 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;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Build.Exceptions;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal class RazorServerManager : IBuildServerManager
{
private readonly IRazorAssemblyResolver _resolver;
private readonly ICommandFactory _commandFactory;
public RazorServerManager(IRazorAssemblyResolver resolver = null, ICommandFactory commandFactory = null)
{
_resolver = resolver ?? new RazorAssemblyResolver();
_commandFactory = commandFactory ?? new DotNetCommandFactory(alwaysRunOutOfProc: true);
}
public string ServerName => LocalizableStrings.RazorServer;
public Task<Result> ShutdownServerAsync()
{
return Task.Run(() => {
try
{
bool haveRazorAssembly = false;
foreach (var toolAssembly in _resolver.EnumerateRazorToolAssemblies())
{
haveRazorAssembly = true;
var command = _commandFactory
.Create("exec", new string[] { toolAssembly.Value, "shutdown" })
.CaptureStdOut()
.CaptureStdErr();
var result = command.Execute();
if (result.ExitCode != 0)
{
return new Result(ResultKind.Failure, result.StdErr);
}
}
if (!haveRazorAssembly)
{
return new Result(ResultKind.Skipped, LocalizableStrings.NoRazorProjectFound);
}
return new Result(ResultKind.Success);
}
catch (InvalidProjectFileException ex)
{
return new Result(ex);
}
});
}
}
}

View file

@ -1,37 +0,0 @@
// 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;
namespace Microsoft.DotNet.BuildServer
{
internal enum ResultKind
{
Success,
Failure,
Skipped
}
internal struct Result
{
public Result(ResultKind kind, string message = null)
{
Kind = kind;
Message = message;
Exception = null;
}
public Result(Exception exception)
{
Kind = ResultKind.Failure;
Message = exception.Message;
Exception = exception;
}
public ResultKind Kind { get; private set; }
public string Message { get; private set; }
public Exception Exception { get; private set; }
}
}

View file

@ -10,7 +10,7 @@ using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.BuildServer
{
internal class VBCSCompilerServerManager : IBuildServerManager
internal class VBCSCompilerServer : IBuildServer
{
internal static readonly string VBCSCompilerPath = Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
@ -20,29 +20,30 @@ namespace Microsoft.DotNet.BuildServer
private readonly ICommandFactory _commandFactory;
public VBCSCompilerServerManager(ICommandFactory commandFactory = null)
public VBCSCompilerServer(ICommandFactory commandFactory = null)
{
_commandFactory = commandFactory ?? new DotNetCommandFactory(alwaysRunOutOfProc: true);
}
public string ServerName => LocalizableStrings.VBCSCompilerServer;
public int ProcessId => 0; // Not yet used
public Task<Result> ShutdownServerAsync()
public string Name => LocalizableStrings.VBCSCompilerServer;
public void Shutdown()
{
return Task.Run(() => {
var command = _commandFactory
.Create("exec", new[] { VBCSCompilerPath, "-shutdown" })
.CaptureStdOut()
.CaptureStdErr();
var command = _commandFactory
.Create("exec", new[] { VBCSCompilerPath, "-shutdown" })
.CaptureStdOut()
.CaptureStdErr();
var result = command.Execute();
if (result.ExitCode != 0)
{
return new Result(ResultKind.Failure, result.StdErr);
}
return new Result(ResultKind.Success);
});
var result = command.Execute();
if (result.ExitCode != 0)
{
throw new BuildServerException(
string.Format(
LocalizableStrings.ShutdownCommandFailed,
result.StdErr));
}
}
}
}

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -17,9 +17,9 @@
<target state="new">Razor build server</target>
<note />
</trans-unit>
<trans-unit id="NoRazorProjectFound">
<source>a Razor project was not found in the current directory.</source>
<target state="new">a Razor project was not found in the current directory.</target>
<trans-unit id="ShutdownCommandFailed">
<source>The shutdown command failed: {0}</source>
<target state="new">The shutdown command failed: {0}</target>
<note />
</trans-unit>
</body>

View file

@ -14,6 +14,8 @@ namespace Microsoft.DotNet.Tools.BuildServer.Shutdown
{
internal class BuildServerShutdownCommand : CommandBase
{
private readonly ServerEnumerationFlags _enumerationFlags;
private readonly IBuildServerProvider _serverProvider;
private readonly bool _useOrderedWait;
private readonly IReporter _reporter;
private readonly IReporter _errorReporter;
@ -21,57 +23,63 @@ namespace Microsoft.DotNet.Tools.BuildServer.Shutdown
public BuildServerShutdownCommand(
AppliedOption options,
ParseResult result,
IEnumerable<IBuildServerManager> managers = null,
IBuildServerProvider serverProvider = null,
bool useOrderedWait = false,
IReporter reporter = null)
: base(result)
{
if (managers == null)
bool msbuild = options.ValueOrDefault<bool>("msbuild");
bool vbcscompiler = options.ValueOrDefault<bool>("vbcscompiler");
bool razor = options.ValueOrDefault<bool>("razor");
bool all = !msbuild && !vbcscompiler && !razor;
_enumerationFlags = ServerEnumerationFlags.None;
if (msbuild || all)
{
bool msbuild = options.ValueOrDefault<bool>("msbuild");
bool vbcscompiler = options.ValueOrDefault<bool>("vbcscompiler");
bool razor = options.ValueOrDefault<bool>("razor");
bool all = !msbuild && !vbcscompiler && !razor;
var enabledManagers = new List<IBuildServerManager>();
if (msbuild || all)
{
enabledManagers.Add(new MSBuildServerManager());
}
if (vbcscompiler || all)
{
enabledManagers.Add(new VBCSCompilerServerManager());
}
if (razor || all)
{
enabledManagers.Add(new RazorServerManager());
}
managers = enabledManagers;
_enumerationFlags |= ServerEnumerationFlags.MSBuild;
}
Managers = managers;
if (vbcscompiler || all)
{
_enumerationFlags |= ServerEnumerationFlags.VBCSCompiler;
}
if (razor || all)
{
_enumerationFlags |= ServerEnumerationFlags.Razor;
}
_serverProvider = serverProvider ?? new BuildServerProvider();
_useOrderedWait = useOrderedWait;
_reporter = reporter ?? Reporter.Output;
_errorReporter = reporter ?? Reporter.Error;
}
public IEnumerable<IBuildServerManager> Managers { get; }
public override int Execute()
{
bool success = true;
var tasks = StartShutdown();
if (tasks.Count == 0)
{
_reporter.WriteLine(LocalizableStrings.NoServersToShutdown.Green());
return 0;
}
bool success = true;
while (tasks.Count > 0)
{
var index = WaitForResult(tasks.Select(t => t.Item2).ToArray());
var (manager, task) = tasks[index];
var (server, task) = tasks[index];
success &= HandleResult(manager, task.Result);
if (task.IsFaulted)
{
success = false;
WriteFailureMessage(server, task.Exception);
}
else
{
WriteSuccessMessage(server);
}
tasks.RemoveAt(index);
}
@ -79,14 +87,15 @@ namespace Microsoft.DotNet.Tools.BuildServer.Shutdown
return success ? 0 : 1;
}
private List<(IBuildServerManager, Task<Result>)> StartShutdown()
private List<(IBuildServer, Task)> StartShutdown()
{
var tasks = new List<(IBuildServerManager, Task<Result>)>();
foreach (var manager in Managers)
var tasks = new List<(IBuildServer, Task)>();
foreach (var server in _serverProvider.EnumerateBuildServers(_enumerationFlags))
{
_reporter.WriteLine(string.Format(LocalizableStrings.ShuttingDownServer, manager.ServerName));
tasks.Add((manager, manager.ShutdownServerAsync()));
WriteShutdownMessage(server);
tasks.Add((server, Task.Run(() => server.Shutdown())));
}
return tasks;
}
@ -94,50 +103,72 @@ namespace Microsoft.DotNet.Tools.BuildServer.Shutdown
{
if (_useOrderedWait)
{
tasks[0].Wait();
return 0;
return Task.WaitAny(tasks.First());
}
return Task.WaitAny(tasks);
}
private bool HandleResult(IBuildServerManager manager, Result result)
private void WriteShutdownMessage(IBuildServer server)
{
switch (result.Kind)
if (server.ProcessId != 0)
{
case ResultKind.Success:
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownSucceeded,
manager.ServerName).Green());
return true;
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShuttingDownServerWithPid,
server.Name,
server.ProcessId));
}
else
{
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShuttingDownServer,
server.Name));
}
}
case ResultKind.Skipped:
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownSkipped,
manager.ServerName,
result.Message).Cyan());
return true;
private void WriteFailureMessage(IBuildServer server, AggregateException exception)
{
if (server.ProcessId != 0)
{
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownFailedWithPid,
server.Name,
server.ProcessId,
exception.InnerException.Message).Red());
}
else
{
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownFailed,
server.Name,
exception.InnerException.Message).Red());
}
case ResultKind.Failure:
_errorReporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownFailed,
manager.ServerName,
result.Message).Red());
if (Reporter.IsVerbose)
{
Reporter.Verbose.WriteLine(exception.ToString().Red());
}
}
if (Reporter.IsVerbose && result.Exception != null)
{
Reporter.Verbose.WriteLine(result.Exception.ToString().Red());
}
return false;
default:
throw new NotSupportedException(
string.Format(
LocalizableStrings.UnsupportedEnumValue,
result.Kind.ToString(),
nameof(ResultKind)));
private void WriteSuccessMessage(IBuildServer server)
{
if (server.ProcessId != 0)
{
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownSucceededWithPid,
server.Name,
server.ProcessId).Green());
}
else
{
_reporter.WriteLine(
string.Format(
LocalizableStrings.ShutDownSucceeded,
server.Name).Green());
}
}
}

View file

@ -132,16 +132,22 @@
<data name="ShuttingDownServer" xml:space="preserve">
<value>Shutting down {0}...</value>
</data>
<data name="ShuttingDownServerWithPid" xml:space="preserve">
<value>Shutting down {0} (process {1})...</value>
</data>
<data name="ShutDownSucceeded" xml:space="preserve">
<value>{0} shut down successfully.</value>
</data>
<data name="ShutDownSucceededWithPid" xml:space="preserve">
<value>{0} (process {1}) shut down successfully.</value>
</data>
<data name="ShutDownFailed" xml:space="preserve">
<value>{0} failed to shut down: {1}</value>
</data>
<data name="ShutDownSkipped" xml:space="preserve">
<value>{0} shut down was skipped: {1}</value>
<data name="ShutDownFailedWithPid" xml:space="preserve">
<value>{0} (process {1}) failed to shut down: {2}</value>
</data>
<data name="UnsupportedEnumValue" xml:space="preserve">
<value>The value '{0}' for enum type '{1}' is not supported.</value>
<data name="NoServersToShutdown" xml:space="preserve">
<value>No build servers are running.</value>
</data>
</root>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -37,14 +37,24 @@
<target state="new">{0} failed to shut down: {1}</target>
<note />
</trans-unit>
<trans-unit id="ShutDownSkipped">
<source>{0} shut down was skipped: {1}</source>
<target state="new">{0} shut down was skipped: {1}</target>
<trans-unit id="ShuttingDownServerWithPid">
<source>Shutting down {0} (process {1})...</source>
<target state="new">Shutting down {0} (process {1})...</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedEnumValue">
<source>The value '{0}' for enum type '{1}' is not supported.</source>
<target state="new">The value '{0}' for enum type '{1}' is not supported.</target>
<trans-unit id="ShutDownSucceededWithPid">
<source>{0} (process {1}) shut down successfully.</source>
<target state="new">{0} (process {1}) shut down successfully.</target>
<note />
</trans-unit>
<trans-unit id="ShutDownFailedWithPid">
<source>{0} (process {1}) failed to shut down: {2}</source>
<target state="new">{0} (process {1}) failed to shut down: {2}</target>
<note />
</trans-unit>
<trans-unit id="NoServersToShutdown">
<source>No build servers are running.</source>
<target state="new">No build servers are running.</target>
<note />
</trans-unit>
</body>

View file

@ -166,7 +166,15 @@ namespace Microsoft.Extensions.DependencyModel.Tests
public IEnumerable<string> EnumerateFiles(string path, string searchPattern)
{
throw new NotImplementedException();
if (searchPattern != "*")
{
throw new NotImplementedException();
}
foreach (var kvp in _files.Where(kvp => kvp.Key != kvp.Value && Path.GetDirectoryName(kvp.Key) == path))
{
yield return kvp.Key;
}
}
public IEnumerable<string> EnumerateFileSystemEntries(string path)

View file

@ -0,0 +1,143 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.BuildServer;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.Extensions.DependencyModel.Tests;
using Moq;
using Xunit;
using LocalizableStrings = Microsoft.DotNet.BuildServer.LocalizableStrings;
namespace Microsoft.DotNet.Tests.BuildServerTests
{
public class BuildServerProviderTests
{
[Fact]
public void GivenMSBuildFlagItYieldsMSBuild()
{
var provider = new BuildServerProvider(
new FileSystemMockBuilder().Build(),
CreateEnvironmentProviderMock().Object);
provider
.EnumerateBuildServers(ServerEnumerationFlags.MSBuild)
.Select(s => s.Name)
.Should()
.Equal(LocalizableStrings.MSBuildServer);
}
[Fact]
public void GivenVBCSCompilerFlagItYieldsVBCSCompiler()
{
var provider = new BuildServerProvider(
new FileSystemMockBuilder().Build(),
CreateEnvironmentProviderMock().Object);
provider
.EnumerateBuildServers(ServerEnumerationFlags.VBCSCompiler)
.Select(s => s.Name)
.Should()
.Equal(LocalizableStrings.VBCSCompilerServer);
}
[Fact]
public void GivenRazorFlagAndNoPidDirectoryTheEnumerationIsEmpty()
{
var provider = new BuildServerProvider(
new FileSystemMockBuilder().Build(),
CreateEnvironmentProviderMock().Object);
provider
.EnumerateBuildServers(ServerEnumerationFlags.Razor)
.Should()
.BeEmpty();
}
[Fact]
public void GivenNoEnvironmentVariableItUsesTheDefaultPidDirectory()
{
var provider = new BuildServerProvider(
new FileSystemMockBuilder().Build(),
CreateEnvironmentProviderMock().Object);
provider
.GetPidFileDirectory()
.Value
.Should()
.Be(Path.Combine(
CliFolderPathCalculator.DotnetUserProfileFolderPath,
"pids",
"build"));
}
[Fact]
public void GivenEnvironmentVariableItUsesItForThePidDirectory()
{
const string PidDirectory = "path/to/some/directory";
var provider = new BuildServerProvider(
new FileSystemMockBuilder().Build(),
CreateEnvironmentProviderMock(PidDirectory).Object);
provider
.GetPidFileDirectory()
.Value
.Should()
.Be(PidDirectory);
}
[Fact]
public void GivenARazorPidFileItReturnsARazorBuildServer()
{
const int ProcessId = 1234;
const string ServerPath = "/path/to/rzc.dll";
const string PipeName = "some-pipe-name";
string pidDirectory = Path.GetFullPath("var/pids/build");
string pidFilePath = Path.Combine(pidDirectory, $"{RazorPidFile.FilePrefix}{ProcessId}");
var fileSystemMock = new FileSystemMockBuilder()
.AddFile(
pidFilePath,
$"{ProcessId}{Environment.NewLine}{RazorPidFile.RazorServerType}{Environment.NewLine}{ServerPath}{Environment.NewLine}{PipeName}")
.AddFile(
Path.Combine(pidDirectory, $"{RazorPidFile.FilePrefix}not-a-pid-file"),
"not-a-pid-file")
.Build();
var provider = new BuildServerProvider(
fileSystemMock,
CreateEnvironmentProviderMock(pidDirectory).Object);
var servers = provider.EnumerateBuildServers(ServerEnumerationFlags.Razor).ToArray();
servers.Length.Should().Be(1);
var razorServer = servers.First() as RazorServer;
razorServer.Should().NotBeNull();
razorServer.ProcessId.Should().Be(ProcessId);
razorServer.Name.Should().Be(LocalizableStrings.RazorServer);
razorServer.PidFile.Should().NotBeNull();
razorServer.PidFile.Path.Value.Should().Be(pidFilePath);
razorServer.PidFile.ProcessId.Should().Be(ProcessId);
razorServer.PidFile.ServerPath.Value.Should().Be(ServerPath);
razorServer.PidFile.PipeName.Should().Be(PipeName);
}
private Mock<IEnvironmentProvider> CreateEnvironmentProviderMock(string value = null)
{
var provider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
provider
.Setup(p => p.GetEnvironmentVariable("DOTNET_BUILD_PIDFILE_DIRECTORY"))
.Returns(value);
return provider;
}
}
}

View file

@ -1,123 +0,0 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Build.Exceptions;
using Microsoft.DotNet.BuildServer;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.Extensions.EnvironmentAbstractions;
using Moq;
using NuGet.Frameworks;
using Xunit;
using LocalizableStrings = Microsoft.DotNet.BuildServer.LocalizableStrings;
namespace Microsoft.DotNet.Tests.BuildServerTests
{
public class RazorServerManagerTests
{
[Fact]
public async Task GivenNoRazorAssemblyShutdownIsSkipped()
{
var resolverMock = new Mock<IRazorAssemblyResolver>(MockBehavior.Strict);
resolverMock.Setup(r => r.EnumerateRazorToolAssemblies()).Returns(new FilePath[] {});
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
var manager = new RazorServerManager(resolverMock.Object, commandFactoryMock.Object);
var result = await manager.ShutdownServerAsync();
result.Kind.Should().Be(ResultKind.Skipped);
result.Message.Should().Be(LocalizableStrings.NoRazorProjectFound);
result.Exception.Should().BeNull();
}
[Fact]
public async Task GivenARazorAssemblyShutdownSucceeds()
{
const string FakeRazorAssemblyPath = "/path/to/razor.dll";
var resolverMock = new Mock<IRazorAssemblyResolver>(MockBehavior.Strict);
resolverMock.Setup(r => r.EnumerateRazorToolAssemblies()).Returns(new FilePath[] { new FilePath(FakeRazorAssemblyPath) });
var commandMock = new Mock<ICommand>(MockBehavior.Strict);
commandMock.Setup(c => c.CaptureStdOut()).Returns(commandMock.Object);
commandMock.Setup(c => c.CaptureStdErr()).Returns(commandMock.Object);
commandMock.Setup(c => c.Execute()).Returns(new CommandResult(null, 0, "", ""));
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
commandFactoryMock
.Setup(
f => f.Create(
"exec",
new string[] { FakeRazorAssemblyPath, "shutdown" },
It.IsAny<NuGetFramework>(),
Constants.DefaultConfiguration))
.Returns(commandMock.Object);
var manager = new RazorServerManager(resolverMock.Object, commandFactoryMock.Object);
var result = await manager.ShutdownServerAsync();
result.Kind.Should().Be(ResultKind.Success);
result.Message.Should().BeNull();
result.Exception.Should().BeNull();
}
[Fact]
public async Task GivenAnInvalidProjectFileShutdownFails()
{
var exception = new InvalidProjectFileException("invalid project!");
var resolverMock = new Mock<IRazorAssemblyResolver>(MockBehavior.Strict);
resolverMock.Setup(r => r.EnumerateRazorToolAssemblies()).Throws(exception);
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
var manager = new RazorServerManager(resolverMock.Object, commandFactoryMock.Object);
var result = await manager.ShutdownServerAsync();
result.Kind.Should().Be(ResultKind.Failure);
result.Message.Should().Be(exception.Message);
result.Exception.Should().Be(exception);
}
[Fact]
public async Task GivenANonZeroExitCodeShutdownFails()
{
const string FakeRazorAssemblyPath = "/path/to/razor.dll";
const string ErrorMessage = "failed!";
var resolverMock = new Mock<IRazorAssemblyResolver>(MockBehavior.Strict);
resolverMock.Setup(r => r.EnumerateRazorToolAssemblies()).Returns(new FilePath[] { new FilePath(FakeRazorAssemblyPath) });
var commandMock = new Mock<ICommand>(MockBehavior.Strict);
commandMock.Setup(c => c.CaptureStdOut()).Returns(commandMock.Object);
commandMock.Setup(c => c.CaptureStdErr()).Returns(commandMock.Object);
commandMock.Setup(c => c.Execute()).Returns(new CommandResult(null, 1, "", ErrorMessage));
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
commandFactoryMock
.Setup(
f => f.Create(
"exec",
new string[] { FakeRazorAssemblyPath, "shutdown" },
It.IsAny<NuGetFramework>(),
Constants.DefaultConfiguration))
.Returns(commandMock.Object);
var manager = new RazorServerManager(resolverMock.Object, commandFactoryMock.Object);
var result = await manager.ShutdownServerAsync();
result.Kind.Should().Be(ResultKind.Failure);
result.Message.Should().Be(ErrorMessage);
result.Exception.Should().BeNull();
}
}
}

View file

@ -0,0 +1,109 @@
// 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;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.BuildServer;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.DependencyModel.Tests;
using Microsoft.Extensions.EnvironmentAbstractions;
using Moq;
using NuGet.Frameworks;
using Xunit;
using LocalizableStrings = Microsoft.DotNet.BuildServer.LocalizableStrings;
namespace Microsoft.DotNet.Tests.BuildServerTests
{
public class RazorServerTests
{
[Fact]
public void GivenAFailedShutdownCommandItThrows()
{
const int ProcessId = 1234;
const string ServerPath = "path/to/rzc.dll";
const string PipeName = "some-pipe-name";
const string ErrorMessage = "error!";
string pidDirectory = Path.GetFullPath("var/pids/build");
string pidFilePath = Path.Combine(pidDirectory, $"{RazorPidFile.FilePrefix}{ProcessId}");
var fileSystemMock = new FileSystemMockBuilder()
.AddFile(pidFilePath, "")
.Build();
fileSystemMock.File.Exists(pidFilePath).Should().BeTrue();
var server = new RazorServer(
pidFile: new RazorPidFile(
path: new FilePath(pidFilePath),
processId: ProcessId,
serverPath: new FilePath(ServerPath),
pipeName: PipeName),
commandFactory: CreateCommandFactoryMock(ServerPath, PipeName, exitCode: 1, stdErr: ErrorMessage).Object,
fileSystem: fileSystemMock);
Action a = () => server.Shutdown();
a.ShouldThrow<BuildServerException>().WithMessage(
string.Format(
LocalizableStrings.ShutdownCommandFailed,
ErrorMessage));
fileSystemMock.File.Exists(pidFilePath).Should().BeTrue();
}
[Fact]
public void GivenASuccessfulShutdownItDoesNotThrow()
{
const int ProcessId = 1234;
const string ServerPath = "path/to/rzc.dll";
const string PipeName = "some-pipe-name";
string pidDirectory = Path.GetFullPath("var/pids/build");
string pidFilePath = Path.Combine(pidDirectory, $"{RazorPidFile.FilePrefix}{ProcessId}");
var fileSystemMock = new FileSystemMockBuilder()
.AddFile(pidFilePath, "")
.Build();
fileSystemMock.File.Exists(pidFilePath).Should().BeTrue();
var server = new RazorServer(
pidFile: new RazorPidFile(
path: new FilePath(pidFilePath),
processId: ProcessId,
serverPath: new FilePath(ServerPath),
pipeName: PipeName),
commandFactory: CreateCommandFactoryMock(ServerPath, PipeName).Object,
fileSystem: fileSystemMock);
server.Shutdown();
fileSystemMock.File.Exists(pidFilePath).Should().BeFalse();
}
private Mock<ICommandFactory> CreateCommandFactoryMock(string serverPath, string pipeName, int exitCode = 0, string stdErr = "")
{
var commandMock = new Mock<ICommand>(MockBehavior.Strict);
commandMock.Setup(c => c.CaptureStdOut()).Returns(commandMock.Object);
commandMock.Setup(c => c.CaptureStdErr()).Returns(commandMock.Object);
commandMock.Setup(c => c.Execute()).Returns(new CommandResult(null, exitCode, "", stdErr));
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
commandFactoryMock
.Setup(
f => f.Create(
"exec",
new string[] { serverPath, "shutdown", "-w", "-p", pipeName },
It.IsAny<NuGetFramework>(),
Constants.DefaultConfiguration))
.Returns(commandMock.Object);
return commandFactoryMock;
}
}
}

View file

@ -1,75 +0,0 @@
// 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;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.BuildServer;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
using Moq;
using NuGet.Frameworks;
using Xunit;
namespace Microsoft.DotNet.Tests.BuildServerTests
{
public class VBCSCompilerServerManagerTests
{
[Fact]
public async Task GivenAZeroExit()
{
var commandMock = new Mock<ICommand>(MockBehavior.Strict);
commandMock.Setup(c => c.CaptureStdOut()).Returns(commandMock.Object);
commandMock.Setup(c => c.CaptureStdErr()).Returns(commandMock.Object);
commandMock.Setup(c => c.Execute()).Returns(new CommandResult(null, 0, "", ""));
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
commandFactoryMock
.Setup(
f => f.Create(
"exec",
new string[] { VBCSCompilerServerManager.VBCSCompilerPath, "-shutdown" },
It.IsAny<NuGetFramework>(),
Constants.DefaultConfiguration))
.Returns(commandMock.Object);
var manager = new VBCSCompilerServerManager(commandFactoryMock.Object);
var result = await manager.ShutdownServerAsync();
result.Kind.Should().Be(ResultKind.Success);
result.Message.Should().BeNull();
result.Exception.Should().BeNull();
}
[Fact]
public async Task GivenANonZeroExitCodeShutdownFails()
{
const string ErrorMessage = "failed!";
var commandMock = new Mock<ICommand>(MockBehavior.Strict);
commandMock.Setup(c => c.CaptureStdOut()).Returns(commandMock.Object);
commandMock.Setup(c => c.CaptureStdErr()).Returns(commandMock.Object);
commandMock.Setup(c => c.Execute()).Returns(new CommandResult(null, 1, "", ErrorMessage));
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
commandFactoryMock
.Setup(
f => f.Create(
"exec",
new string[] { VBCSCompilerServerManager.VBCSCompilerPath, "-shutdown" },
It.IsAny<NuGetFramework>(),
Constants.DefaultConfiguration))
.Returns(commandMock.Object);
var manager = new VBCSCompilerServerManager(commandFactoryMock.Object);
var result = await manager.ShutdownServerAsync();
result.Kind.Should().Be(ResultKind.Failure);
result.Message.Should().Be(ErrorMessage);
result.Exception.Should().BeNull();
}
}
}

View file

@ -0,0 +1,63 @@
// 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;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.BuildServer;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
using Moq;
using NuGet.Frameworks;
using Xunit;
using LocalizableStrings = Microsoft.DotNet.BuildServer.LocalizableStrings;
namespace Microsoft.DotNet.Tests.BuildServerTests
{
public class VBCSCompilerServerTests
{
[Fact]
public void GivenAZeroExitShutdownDoesNotThrow()
{
var server = new VBCSCompilerServer(CreateCommandFactoryMock().Object);
server.Shutdown();
}
[Fact]
public void GivenANonZeroExitCodeShutdownThrows()
{
const string ErrorMessage = "failed!";
var server = new VBCSCompilerServer(CreateCommandFactoryMock(exitCode: 1, stdErr: ErrorMessage).Object);
Action a = () => server.Shutdown();
a.ShouldThrow<BuildServerException>().WithMessage(
string.Format(
LocalizableStrings.ShutdownCommandFailed,
ErrorMessage));
}
private Mock<ICommandFactory> CreateCommandFactoryMock(int exitCode = 0, string stdErr = "")
{
var commandMock = new Mock<ICommand>(MockBehavior.Strict);
commandMock.Setup(c => c.CaptureStdOut()).Returns(commandMock.Object);
commandMock.Setup(c => c.CaptureStdErr()).Returns(commandMock.Object);
commandMock.Setup(c => c.Execute()).Returns(new CommandResult(null, exitCode, "", stdErr));
var commandFactoryMock = new Mock<ICommandFactory>(MockBehavior.Strict);
commandFactoryMock
.Setup(
f => f.Create(
"exec",
new string[] { VBCSCompilerServer.VBCSCompilerPath, "-shutdown" },
It.IsAny<NuGetFramework>(),
Constants.DefaultConfiguration))
.Returns(commandMock.Object);
return commandFactoryMock;
}
}
}

View file

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.BuildServer;
using Microsoft.DotNet.Cli;
@ -26,57 +25,92 @@ namespace Microsoft.DotNet.Tests.Commands
private readonly BufferedReporter _reporter = new BufferedReporter();
[Fact]
public void GivenNoOptionsAllManagersArePresent()
public void GivenNoOptionsItEnumeratesAllServers()
{
var command = CreateCommand();
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
command.Managers.Select(m => m.ServerName).Should().Equal(
DotNet.BuildServer.LocalizableStrings.MSBuildServer,
DotNet.BuildServer.LocalizableStrings.VBCSCompilerServer,
DotNet.BuildServer.LocalizableStrings.RazorServer
);
provider
.Setup(p => p.EnumerateBuildServers(ServerEnumerationFlags.All))
.Returns(Array.Empty<IBuildServer>());
var command = CreateCommand(serverProvider: provider.Object);
command.Execute().Should().Be(0);
_reporter.Lines.Should().Equal(LocalizableStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.All), Times.Once);
}
[Fact]
public void GivenMSBuildOptionOnlyItIsTheOnlyManager()
public void GivenMSBuildOptionOnlyItEnumeratesOnlyMSBuildServers()
{
var command = CreateCommand("--msbuild");
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
command.Managers.Select(m => m.ServerName).Should().Equal(
DotNet.BuildServer.LocalizableStrings.MSBuildServer
);
provider
.Setup(p => p.EnumerateBuildServers(ServerEnumerationFlags.MSBuild))
.Returns(Array.Empty<IBuildServer>());
var command = CreateCommand(options: "--msbuild", serverProvider: provider.Object);
command.Execute().Should().Be(0);
_reporter.Lines.Should().Equal(LocalizableStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.MSBuild), Times.Once);
}
[Fact]
public void GivenVBCSCompilerOptionOnlyItIsTheOnlyManager()
public void GivenVBCSCompilerOptionOnlyItEnumeratesOnlyVBCSCompilers()
{
var command = CreateCommand("--vbcscompiler");
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
command.Managers.Select(m => m.ServerName).Should().Equal(
DotNet.BuildServer.LocalizableStrings.VBCSCompilerServer
);
provider
.Setup(p => p.EnumerateBuildServers(ServerEnumerationFlags.VBCSCompiler))
.Returns(Array.Empty<IBuildServer>());
var command = CreateCommand(options: "--vbcscompiler", serverProvider: provider.Object);
command.Execute().Should().Be(0);
_reporter.Lines.Should().Equal(LocalizableStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.VBCSCompiler), Times.Once);
}
[Fact]
public void GivenRazorOptionOnlyItIsTheOnlyManager()
public void GivenRazorOptionOnlyItEnumeratesOnlyRazorServers()
{
var command = CreateCommand("--razor");
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
command.Managers.Select(m => m.ServerName).Should().Equal(
DotNet.BuildServer.LocalizableStrings.RazorServer
);
provider
.Setup(p => p.EnumerateBuildServers(ServerEnumerationFlags.Razor))
.Returns(Array.Empty<IBuildServer>());
var command = CreateCommand(options: "--razor", serverProvider: provider.Object);
command.Execute().Should().Be(0);
_reporter.Lines.Should().Equal(LocalizableStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.Razor), Times.Once);
}
[Fact]
public void GivenSuccessfulShutdownsItPrintsSuccess()
{
var mocks = new[] {
CreateManagerMock("first", new Result(ResultKind.Success)),
CreateManagerMock("second", new Result(ResultKind.Success)),
CreateManagerMock("third", new Result(ResultKind.Success))
CreateServerMock("first"),
CreateServerMock("second"),
CreateServerMock("third")
};
var command = CreateCommand(managers: mocks.Select(m => m.Object));
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
provider
.Setup(p => p.EnumerateBuildServers(ServerEnumerationFlags.All))
.Returns(mocks.Select(m => m.Object));
var command = CreateCommand(serverProvider: provider.Object);
command.Execute().Should().Be(0);
@ -94,15 +128,21 @@ namespace Microsoft.DotNet.Tests.Commands
[Fact]
public void GivenAFailingShutdownItPrintsFailureMessage()
{
const string FailureMessage = "failed!";
const string FirstFailureMessage = "first failed!";
const string ThirdFailureMessage = "third failed!";
var mocks = new[] {
CreateManagerMock("first", new Result(ResultKind.Success)),
CreateManagerMock("second", new Result(ResultKind.Failure, FailureMessage)),
CreateManagerMock("third", new Result(ResultKind.Success))
CreateServerMock("first", exceptionMessage: FirstFailureMessage),
CreateServerMock("second"),
CreateServerMock("third", exceptionMessage: ThirdFailureMessage)
};
var command = CreateCommand(managers: mocks.Select(m => m.Object));
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
provider
.Setup(p => p.EnumerateBuildServers(ServerEnumerationFlags.All))
.Returns(mocks.Select(m => m.Object));
var command = CreateCommand(serverProvider: provider.Object);
command.Execute().Should().Be(1);
@ -110,113 +150,80 @@ namespace Microsoft.DotNet.Tests.Commands
FormatShuttingDownMessage(mocks[0].Object),
FormatShuttingDownMessage(mocks[1].Object),
FormatShuttingDownMessage(mocks[2].Object),
FormatSuccessMessage(mocks[0].Object),
FormatFailureMessage(mocks[1].Object, FailureMessage),
FormatSuccessMessage(mocks[2].Object));
VerifyShutdownCalls(mocks);
}
[Fact]
public void GivenASkippedShutdownItPrintsSkipMessage()
{
const string SkipMessage = "skipped!";
var mocks = new[] {
CreateManagerMock("first", new Result(ResultKind.Success)),
CreateManagerMock("second", new Result(ResultKind.Success)),
CreateManagerMock("third", new Result(ResultKind.Skipped, SkipMessage))
};
var command = CreateCommand(managers: mocks.Select(m => m.Object));
command.Execute().Should().Be(0);
_reporter.Lines.Should().Equal(
FormatShuttingDownMessage(mocks[0].Object),
FormatShuttingDownMessage(mocks[1].Object),
FormatShuttingDownMessage(mocks[2].Object),
FormatSuccessMessage(mocks[0].Object),
FormatFailureMessage(mocks[0].Object, FirstFailureMessage),
FormatSuccessMessage(mocks[1].Object),
FormatSkippedMessage(mocks[2].Object, SkipMessage));
FormatFailureMessage(mocks[2].Object, ThirdFailureMessage));
VerifyShutdownCalls(mocks);
}
[Fact]
public void GivenSuccessFailureAndSkippedItPrintsAllThree()
{
const string FailureMessage = "failed!";
const string SkipMessage = "skipped!";
var mocks = new[] {
CreateManagerMock("first", new Result(ResultKind.Success)),
CreateManagerMock("second", new Result(ResultKind.Failure, FailureMessage)),
CreateManagerMock("third", new Result(ResultKind.Skipped, SkipMessage))
};
var command = CreateCommand(managers: mocks.Select(m => m.Object));
command.Execute().Should().Be(1);
_reporter.Lines.Should().Equal(
FormatShuttingDownMessage(mocks[0].Object),
FormatShuttingDownMessage(mocks[1].Object),
FormatShuttingDownMessage(mocks[2].Object),
FormatSuccessMessage(mocks[0].Object),
FormatFailureMessage(mocks[1].Object, FailureMessage),
FormatSkippedMessage(mocks[2].Object, SkipMessage));
VerifyShutdownCalls(mocks);
}
private BuildServerShutdownCommand CreateCommand(string options = "", IEnumerable<IBuildServerManager> managers = null)
private BuildServerShutdownCommand CreateCommand(
string options = "",
IBuildServerProvider serverProvider = null,
IEnumerable<IBuildServer> buildServers = null,
ServerEnumerationFlags expectedFlags = ServerEnumerationFlags.None)
{
ParseResult result = Parser.Instance.Parse("dotnet build-server shutdown " + options);
return new BuildServerShutdownCommand(
options: result["dotnet"]["build-server"]["shutdown"],
result: result,
managers: managers,
serverProvider: serverProvider,
useOrderedWait: true,
reporter: _reporter);
}
private Mock<IBuildServerManager> CreateManagerMock(string serverName, Result result)
private Mock<IBuildServer> CreateServerMock(string name, int pid = 0, string exceptionMessage = null)
{
var mock = new Mock<IBuildServerManager>(MockBehavior.Strict);
var mock = new Mock<IBuildServer>(MockBehavior.Strict);
mock.SetupGet(m => m.ServerName).Returns(serverName);
mock.Setup(m => m.ShutdownServerAsync()).Returns(Task.FromResult(result));
mock.SetupGet(s => s.ProcessId).Returns(pid);
mock.SetupGet(s => s.Name).Returns(name);
if (exceptionMessage == null)
{
mock.Setup(s => s.Shutdown());
}
else
{
mock.Setup(s => s.Shutdown()).Throws(new Exception(exceptionMessage));
}
return mock;
}
private void VerifyShutdownCalls(IEnumerable<Mock<IBuildServerManager>> mocks)
private void VerifyShutdownCalls(IEnumerable<Mock<IBuildServer>> mocks)
{
foreach (var mock in mocks)
{
mock.Verify(m => m.ShutdownServerAsync(), Times.Once());
mock.Verify(s => s.Shutdown(), Times.Once);
}
}
private static string FormatShuttingDownMessage(IBuildServerManager manager)
private static string FormatShuttingDownMessage(IBuildServer server)
{
return string.Format(LocalizableStrings.ShuttingDownServer, manager.ServerName);
if (server.ProcessId != 0)
{
return string.Format(LocalizableStrings.ShuttingDownServerWithPid, server.Name, server.ProcessId);
}
return string.Format(LocalizableStrings.ShuttingDownServer, server.Name);
}
private static string FormatSuccessMessage(IBuildServerManager manager)
private static string FormatSuccessMessage(IBuildServer server)
{
return string.Format(LocalizableStrings.ShutDownSucceeded, manager.ServerName).Green();
if (server.ProcessId != 0)
{
return string.Format(LocalizableStrings.ShutDownSucceededWithPid, server.Name, server.ProcessId).Green();
}
return string.Format(LocalizableStrings.ShutDownSucceeded, server.Name).Green();
}
private static string FormatFailureMessage(IBuildServerManager manager, string message)
private static string FormatFailureMessage(IBuildServer server, string message)
{
return string.Format(LocalizableStrings.ShutDownFailed, manager.ServerName, message).Red();
}
private static string FormatSkippedMessage(IBuildServerManager manager, string message)
{
return string.Format(LocalizableStrings.ShutDownSkipped, manager.ServerName, message).Cyan();
if (server.ProcessId != 0)
{
return string.Format(LocalizableStrings.ShutDownFailedWithPid, server.Name, server.ProcessId, message).Red();
}
return string.Format(LocalizableStrings.ShutDownFailed, server.Name, message).Red();
}
}
}