SB smoke tests: test the published output (#16858)
This commit is contained in:
parent
49b8561691
commit
ed5150ffa1
5 changed files with 125 additions and 33 deletions
|
@ -3,9 +3,15 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
|
||||
|
@ -98,13 +104,14 @@ internal class DotNetHelper
|
|||
}
|
||||
}
|
||||
|
||||
public void ExecuteCmd(string args, string? workingDirectory = null, Action<Process>? additionalProcessConfigCallback = null, int? expectedExitCode = 0, int millisecondTimeout = -1)
|
||||
public void ExecuteCmd(string args, string? workingDirectory = null, Action<Process>? processConfigCallback = null,
|
||||
int? expectedExitCode = 0, int millisecondTimeout = -1)
|
||||
{
|
||||
(Process Process, string StdOut, string StdErr) executeResult = ExecuteHelper.ExecuteProcess(
|
||||
DotNetPath,
|
||||
args,
|
||||
OutputHelper,
|
||||
configure: (process) => configureProcess(process, workingDirectory),
|
||||
configureCallback: (process) => configureProcess(process, workingDirectory),
|
||||
millisecondTimeout: millisecondTimeout);
|
||||
|
||||
if (expectedExitCode != null) {
|
||||
|
@ -115,7 +122,7 @@ internal class DotNetHelper
|
|||
{
|
||||
ConfigureProcess(process, workingDirectory);
|
||||
|
||||
additionalProcessConfigCallback?.Invoke(process);
|
||||
processConfigCallback?.Invoke(process);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +168,7 @@ internal class DotNetHelper
|
|||
return projectDirectory;
|
||||
}
|
||||
|
||||
public void ExecutePublish(string projectName, bool? selfContained = null, string? rid = null, bool trimmed = false, bool readyToRun = false)
|
||||
public void ExecutePublish(string projectName, DotNetTemplate template, bool? selfContained = null, string? rid = null, bool trimmed = false, bool readyToRun = false)
|
||||
{
|
||||
string options = string.Empty;
|
||||
string binlogDifferentiator = string.Empty;
|
||||
|
@ -190,40 +197,66 @@ internal class DotNetHelper
|
|||
}
|
||||
}
|
||||
|
||||
string projDir = GetProjectDirectory(projectName);
|
||||
string publishDir = Path.Combine(projDir, "bin", "publish");
|
||||
|
||||
ExecuteCmd(
|
||||
$"publish {options} {GetBinLogOption(projectName, "publish", binlogDifferentiator)}",
|
||||
GetProjectDirectory(projectName));
|
||||
$"publish {options} {GetBinLogOption(projectName, "publish", binlogDifferentiator)} -o {publishDir}",
|
||||
projDir);
|
||||
|
||||
if (template == DotNetTemplate.Console)
|
||||
{
|
||||
ExecuteCmd($"{projectName}.dll", publishDir, expectedExitCode: 0);
|
||||
}
|
||||
else if (template == DotNetTemplate.ClassLib || template == DotNetTemplate.BlazorWasm)
|
||||
{
|
||||
// Can't run the published output of classlib (no entrypoint) or WASM (needs a server)
|
||||
}
|
||||
// Assume it is a web-based template
|
||||
else
|
||||
{
|
||||
ExecuteWebDll(projectName, publishDir, template);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteRun(string projectName) =>
|
||||
ExecuteCmd($"run {GetBinLogOption(projectName, "run")}", GetProjectDirectory(projectName));
|
||||
|
||||
public void ExecuteRunWeb(string projectName)
|
||||
public void ExecuteRunWeb(string projectName, DotNetTemplate template)
|
||||
{
|
||||
// 'dotnet run' exit code differs between CoreCLR and Mono (https://github.com/dotnet/sdk/issues/30095).
|
||||
int expectedExitCode = IsMonoRuntime ? 143 : 0;
|
||||
ExecuteCmd(
|
||||
$"run {GetBinLogOption(projectName, "run")}",
|
||||
GetProjectDirectory(projectName),
|
||||
additionalProcessConfigCallback: processConfigCallback,
|
||||
expectedExitCode,
|
||||
millisecondTimeout: 30000);
|
||||
|
||||
void processConfigCallback(Process process)
|
||||
{
|
||||
process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
|
||||
{
|
||||
if (e.Data?.Contains("Application started. Press Ctrl+C to shut down.") ?? false)
|
||||
{
|
||||
ExecuteHelper.ExecuteProcessValidateExitCode("kill", $"-s TERM {process.Id}", OutputHelper);
|
||||
}
|
||||
});
|
||||
}
|
||||
ExecuteWeb(
|
||||
projectName,
|
||||
$"run --no-launch-profile {GetBinLogOption(projectName, "run")}",
|
||||
GetProjectDirectory(projectName),
|
||||
template,
|
||||
expectedExitCode);
|
||||
}
|
||||
|
||||
public void ExecuteWebDll(string projectName, string workingDirectory, DotNetTemplate template) =>
|
||||
ExecuteWeb(projectName, $"{projectName}.dll", workingDirectory, template, expectedExitCode: 0);
|
||||
|
||||
public void ExecuteTest(string projectName) =>
|
||||
ExecuteCmd($"test {GetBinLogOption(projectName, "test")}", GetProjectDirectory(projectName));
|
||||
|
||||
private void ExecuteWeb(string projectName, string args, string workingDirectory, DotNetTemplate template, int expectedExitCode)
|
||||
{
|
||||
WebAppValidator validator = new(OutputHelper, template);
|
||||
ExecuteCmd(
|
||||
args,
|
||||
workingDirectory,
|
||||
processConfigCallback: validator.Validate,
|
||||
expectedExitCode: expectedExitCode,
|
||||
millisecondTimeout: 30000);
|
||||
Assert.True(validator.IsValidated);
|
||||
if (validator.ValidationException is not null)
|
||||
{
|
||||
throw validator.ValidationException;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBinLogOption(string projectName, string command, string? differentiator = null)
|
||||
{
|
||||
string fileName = $"{projectName}-{command}";
|
||||
|
@ -256,4 +289,62 @@ internal class DotNetHelper
|
|||
}
|
||||
|
||||
private static string GetProjectDirectory(string projectName) => Path.Combine(ProjectsDirectory, projectName);
|
||||
|
||||
private class WebAppValidator
|
||||
{
|
||||
private readonly ITestOutputHelper _outputHelper;
|
||||
private readonly DotNetTemplate _template;
|
||||
|
||||
public WebAppValidator(ITestOutputHelper outputHelper, DotNetTemplate template)
|
||||
{
|
||||
_outputHelper = outputHelper;
|
||||
_template = template;
|
||||
}
|
||||
|
||||
public bool IsValidated { get; set; }
|
||||
public Exception? ValidationException { get; set; }
|
||||
|
||||
private static int GetAvailablePort()
|
||||
{
|
||||
TcpListener listener = new(IPAddress.Loopback, 0);
|
||||
listener.Start();
|
||||
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
|
||||
public void Validate(Process process)
|
||||
{
|
||||
int port = GetAvailablePort();
|
||||
process.StartInfo.EnvironmentVariables.Add("ASPNETCORE_HTTP_PORTS", port.ToString());
|
||||
process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Data?.Contains("Application started. Press Ctrl+C to shut down.") ?? false)
|
||||
{
|
||||
_outputHelper.WriteLine("Detected app has started. Sending web request to validate...");
|
||||
|
||||
using HttpClient httpClient = new();
|
||||
string url = $"http://localhost:{port}";
|
||||
if (_template == DotNetTemplate.WebApi)
|
||||
{
|
||||
url += "/WeatherForecast";
|
||||
}
|
||||
|
||||
using HttpResponseMessage resultMsg = httpClient.GetAsync(new Uri(url)).Result;
|
||||
_outputHelper.WriteLine($"Status code returned: {resultMsg.StatusCode}");
|
||||
resultMsg.EnsureSuccessStatusCode();
|
||||
IsValidated = true;
|
||||
|
||||
ExecuteHelper.ExecuteProcessValidateExitCode("kill", $"-s TERM {process.Id}", _outputHelper);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ValidationException = ex;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public class DotNetWatchTests : SmokeTests
|
|||
DotNetHelper.ExecuteCmd(
|
||||
"watch run",
|
||||
workingDirectory: projectDirectory,
|
||||
additionalProcessConfigCallback: processConfigCallback,
|
||||
processConfigCallback: processConfigCallback,
|
||||
expectedExitCode: null, // The exit code does not reflect whether or not dotnet watch is working properly
|
||||
millisecondTimeout: 60000);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ internal static class ExecuteHelper
|
|||
string args,
|
||||
ITestOutputHelper outputHelper,
|
||||
bool logOutput = false,
|
||||
Action<Process>? configure = null,
|
||||
Action<Process>? configureCallback = null,
|
||||
int millisecondTimeout = -1)
|
||||
{
|
||||
outputHelper.WriteLine($"Executing: {fileName} {args}");
|
||||
|
@ -41,7 +41,7 @@ internal static class ExecuteHelper
|
|||
process.StartInfo.Environment.Remove(key);
|
||||
}
|
||||
|
||||
configure?.Invoke(process);
|
||||
configureCallback?.Invoke(process);
|
||||
|
||||
StringBuilder stdOutput = new();
|
||||
process.OutputDataReceived += new DataReceivedEventHandler(
|
||||
|
@ -64,6 +64,7 @@ internal static class ExecuteHelper
|
|||
});
|
||||
|
||||
process.Start();
|
||||
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
process.WaitForExit(millisecondTimeout);
|
||||
|
|
|
@ -48,7 +48,7 @@ public class OmniSharpTests : SmokeTests
|
|||
OutputHelper,
|
||||
logOutput: true,
|
||||
millisecondTimeout: 5000,
|
||||
configure: (process) => DotNetHelper.ConfigureProcess(process, projectDirectory, setPath: true));
|
||||
configureCallback: (process) => DotNetHelper.ConfigureProcess(process, projectDirectory, setPath: true));
|
||||
|
||||
Assert.NotEqual(0, executeResult.Process.ExitCode);
|
||||
Assert.DoesNotContain("ERROR", executeResult.StdOut);
|
||||
|
|
|
@ -35,7 +35,7 @@ public class TestScenario
|
|||
{
|
||||
if (Template.IsAspNetCore())
|
||||
{
|
||||
dotNetHelper.ExecuteRunWeb(projectName);
|
||||
dotNetHelper.ExecuteRunWeb(projectName, Template);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -44,17 +44,17 @@ public class TestScenario
|
|||
}
|
||||
if (Commands.HasFlag(DotNetActions.Publish))
|
||||
{
|
||||
dotNetHelper.ExecutePublish(projectName);
|
||||
dotNetHelper.ExecutePublish(projectName, Template);
|
||||
}
|
||||
if (Commands.HasFlag(DotNetActions.PublishComplex))
|
||||
{
|
||||
dotNetHelper.ExecutePublish(projectName, selfContained: false);
|
||||
dotNetHelper.ExecutePublish(projectName, selfContained: true, Config.TargetRid);
|
||||
dotNetHelper.ExecutePublish(projectName, selfContained: true, $"linux-{Config.TargetArchitecture}");
|
||||
dotNetHelper.ExecutePublish(projectName, Template, selfContained: false);
|
||||
dotNetHelper.ExecutePublish(projectName, Template, selfContained: true, rid: Config.TargetRid);
|
||||
dotNetHelper.ExecutePublish(projectName, Template, selfContained: true, rid: $"linux-{Config.TargetArchitecture}");
|
||||
}
|
||||
if (Commands.HasFlag(DotNetActions.PublishR2R))
|
||||
{
|
||||
dotNetHelper.ExecutePublish(projectName, selfContained: true, $"linux-{Config.TargetArchitecture}", trimmed: true, readyToRun: true);
|
||||
dotNetHelper.ExecutePublish(projectName, Template, selfContained: true, rid: $"linux-{Config.TargetArchitecture}", trimmed: true, readyToRun: true);
|
||||
}
|
||||
if (Commands.HasFlag(DotNetActions.Test))
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue