|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.CommandLine;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.BuildServer;
using Microsoft.DotNet.Cli.Commands;
using Microsoft.DotNet.Cli.Commands.BuildServer.Shutdown;
using Microsoft.DotNet.Cli.Utils.Extensions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.Extensions.EnvironmentAbstractions;
using Moq;
using Parser = Microsoft.DotNet.Cli.Parser;
namespace Microsoft.DotNet.Tests.Commands
{
public class BuildServerShutdownCommandTests : SdkTest
{
public BuildServerShutdownCommandTests(ITestOutputHelper log) : base(log)
{
}
private readonly BufferedReporter _reporter = new();
[Fact]
public void GivenNoOptionsItEnumeratesAllServers()
{
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
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(CliCommandStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.All), Times.Once);
}
[Fact]
public void GivenMSBuildOptionOnlyItEnumeratesOnlyMSBuildServers()
{
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
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(CliCommandStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.MSBuild), Times.Once);
}
[Fact]
public void GivenVBCSCompilerOptionOnlyItEnumeratesOnlyVBCSCompilers()
{
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
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(CliCommandStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.VBCSCompiler), Times.Once);
}
[Fact]
public void GivenRazorOptionOnlyItEnumeratesOnlyRazorServers()
{
var provider = new Mock<IBuildServerProvider>(MockBehavior.Strict);
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(CliCommandStrings.NoServersToShutdown.Green());
provider.Verify(p => p.EnumerateBuildServers(ServerEnumerationFlags.Razor), Times.Once);
}
[Fact]
public void GivenSuccessfulShutdownsItPrintsSuccess()
{
var mocks = new[] {
CreateServerMock("first"),
CreateServerMock("second"),
CreateServerMock("third")
};
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);
_reporter.Lines.Should().Equal(
FormatShuttingDownMessage(mocks[0].Object),
FormatShuttingDownMessage(mocks[1].Object),
FormatShuttingDownMessage(mocks[2].Object),
FormatSuccessMessage(mocks[0].Object),
FormatSuccessMessage(mocks[1].Object),
FormatSuccessMessage(mocks[2].Object));
VerifyShutdownCalls(mocks);
}
[Fact]
public void GivenAFailingShutdownItPrintsFailureMessage()
{
const string FirstFailureMessage = "first failed!";
const string ThirdFailureMessage = "third failed!";
var mocks = new[] {
CreateServerMock("first", exceptionMessage: FirstFailureMessage),
CreateServerMock("second"),
CreateServerMock("third", exceptionMessage: ThirdFailureMessage)
};
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);
_reporter.Lines.Should().Equal(
FormatShuttingDownMessage(mocks[0].Object),
FormatShuttingDownMessage(mocks[1].Object),
FormatShuttingDownMessage(mocks[2].Object),
FormatFailureMessage(mocks[0].Object, FirstFailureMessage),
FormatSuccessMessage(mocks[1].Object),
FormatFailureMessage(mocks[2].Object, ThirdFailureMessage));
VerifyShutdownCalls(mocks);
}
[Fact(Skip = "https://github.com/dotnet/sdk/issues/3684")]
public void GivenARunningRazorServerItShutsDownSuccessfully()
{
var pipeName = Path.GetRandomFileName();
var pidDirectory = _testAssetsManager.CreateTestDirectory(identifier: "pidDirectory").Path;
var testInstance = _testAssetsManager
.CopyTestAsset("TestRazorApp")
.WithSource();
new BuildCommand(testInstance)
.WithEnvironmentVariable(BuildServerProvider.PidFileDirectoryVariableName, pidDirectory)
.Execute($"/p:_RazorBuildServerPipeName={pipeName}")
.Should()
.Pass();
var files = Directory.GetFiles(pidDirectory, RazorPidFile.FilePrefix + "*");
files.Length.Should().Be(1);
var pidFile = RazorPidFile.Read(new FilePath(files.First()));
pidFile.PipeName.Should().Be(pipeName);
new BuildServerCommand(Log)
.WithWorkingDirectory(testInstance.TestRoot)
.WithEnvironmentVariable(BuildServerProvider.PidFileDirectoryVariableName, pidDirectory)
.Execute("shutdown", "--razor")
.Should()
.Pass()
.And
.HaveStdOutContaining(
string.Format(
CliCommandStrings.ShutDownSucceededWithPid,
CliStrings.RazorServer,
pidFile.ProcessId));
}
private BuildServerShutdownCommand CreateCommand(
ReadOnlySpan<string> options = default,
IBuildServerProvider serverProvider = null,
IEnumerable<IBuildServer> buildServers = null,
ServerEnumerationFlags expectedFlags = ServerEnumerationFlags.None)
{
ParseResult result = Parser.Parse(["dotnet", "build-server", "shutdown", .. options]);
return new BuildServerShutdownCommand(
result: result,
serverProvider: serverProvider,
useOrderedWait: true,
reporter: _reporter);
}
private Mock<IBuildServer> CreateServerMock(string name, int pid = 0, string exceptionMessage = null)
{
var mock = new Mock<IBuildServer>(MockBehavior.Strict);
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<IBuildServer>> mocks)
{
foreach (var mock in mocks)
{
mock.Verify(s => s.Shutdown(), Times.Once);
}
}
private static string FormatShuttingDownMessage(IBuildServer server)
{
if (server.ProcessId != 0)
{
return string.Format(CliCommandStrings.ShuttingDownServerWithPid, server.Name, server.ProcessId);
}
return string.Format(CliCommandStrings.ShuttingDownServer, server.Name);
}
private static string FormatSuccessMessage(IBuildServer server)
{
if (server.ProcessId != 0)
{
return string.Format(CliCommandStrings.ShutDownSucceededWithPid, server.Name, server.ProcessId).Green();
}
return string.Format(CliCommandStrings.ShutDownSucceeded, server.Name).Green();
}
private static string FormatFailureMessage(IBuildServer server, string message)
{
if (server.ProcessId != 0)
{
return string.Format(CliCommandStrings.ShutDownFailedWithPid, server.Name, server.ProcessId, message).Red();
}
return string.Format(CliCommandStrings.ShutDownFailed, server.Name, message).Red();
}
}
}
|