2022-01-21 07:59:36 -06:00
|
|
|
// Licensed to the .NET Foundation under one or more agreements.
|
|
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.IO;
|
2022-09-08 14:23:06 -05:00
|
|
|
using System.Linq;
|
2022-01-21 07:59:36 -06:00
|
|
|
using Xunit.Abstractions;
|
|
|
|
|
|
|
|
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
|
|
|
|
|
|
|
|
internal class DotNetHelper
|
|
|
|
{
|
2022-03-03 08:02:48 -06:00
|
|
|
private static readonly object s_lockObj = new();
|
2022-02-10 15:58:01 -06:00
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
public static string DotNetPath { get; } = Path.Combine(Config.DotNetDirectory, "dotnet");
|
|
|
|
public static string LogsDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), "logs");
|
|
|
|
public static string PackagesDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), "packages");
|
|
|
|
public static string ProjectsDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), $"projects-{DateTime.Now:yyyyMMddHHmmssffff}");
|
|
|
|
|
|
|
|
private ITestOutputHelper OutputHelper { get; }
|
2022-01-21 07:59:36 -06:00
|
|
|
|
|
|
|
public DotNetHelper(ITestOutputHelper outputHelper)
|
|
|
|
{
|
2022-03-03 08:02:48 -06:00
|
|
|
OutputHelper = outputHelper;
|
|
|
|
|
2022-02-10 15:58:01 -06:00
|
|
|
lock (s_lockObj)
|
2022-01-21 07:59:36 -06:00
|
|
|
{
|
2022-02-10 15:58:01 -06:00
|
|
|
if (!Directory.Exists(Config.DotNetDirectory))
|
2022-01-21 07:59:36 -06:00
|
|
|
{
|
2022-03-03 08:02:48 -06:00
|
|
|
if (!File.Exists(Config.SdkTarballPath))
|
2022-02-10 15:58:01 -06:00
|
|
|
{
|
2022-03-03 08:02:48 -06:00
|
|
|
throw new InvalidOperationException($"Tarball path '{Config.SdkTarballPath}' specified in {Config.SdkTarballPath} does not exist.");
|
2022-02-10 15:58:01 -06:00
|
|
|
}
|
2022-01-21 07:59:36 -06:00
|
|
|
|
2022-02-10 15:58:01 -06:00
|
|
|
Directory.CreateDirectory(Config.DotNetDirectory);
|
2022-03-03 08:02:48 -06:00
|
|
|
ExecuteHelper.ExecuteProcessValidateExitCode("tar", $"xzf {Config.SdkTarballPath} -C {Config.DotNetDirectory}", outputHelper);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Directory.Exists(ProjectsDirectory))
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(ProjectsDirectory);
|
|
|
|
InitNugetConfig();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Directory.Exists(PackagesDirectory))
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(PackagesDirectory);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Directory.Exists(LogsDirectory))
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(LogsDirectory);
|
2022-02-10 15:58:01 -06:00
|
|
|
}
|
2022-01-21 07:59:36 -06:00
|
|
|
}
|
2022-03-03 08:02:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
private static void InitNugetConfig()
|
|
|
|
{
|
|
|
|
bool useLocalPackages = !string.IsNullOrEmpty(Config.PrereqsPath);
|
|
|
|
string nugetConfigPrefix = useLocalPackages ? "local" : "online";
|
|
|
|
string nugetConfigPath = Path.Combine(ProjectsDirectory, "NuGet.Config");
|
|
|
|
File.Copy(
|
|
|
|
Path.Combine(BaselineHelper.GetAssetsDirectory(), $"{nugetConfigPrefix}.NuGet.Config"),
|
|
|
|
nugetConfigPath);
|
2022-01-21 07:59:36 -06:00
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
if (useLocalPackages)
|
|
|
|
{
|
2022-09-08 14:23:06 -05:00
|
|
|
// When using local packages this feed is always required. It contains packages that are
|
|
|
|
// not produced by source-build but are required by the various project templates.
|
2022-03-03 08:02:48 -06:00
|
|
|
if (!Directory.Exists(Config.PrereqsPath))
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
$"Prereqs path '{Config.PrereqsPath}' specified in {Config.PrereqsPathEnv} does not exist.");
|
|
|
|
}
|
|
|
|
|
|
|
|
string nugetConfig = File.ReadAllText(nugetConfigPath);
|
|
|
|
nugetConfig = nugetConfig.Replace("SMOKE_TEST_PACKAGE_FEED", Config.PrereqsPath);
|
2022-09-08 14:23:06 -05:00
|
|
|
|
|
|
|
// This package feed is optional. You can use an additional feed of source-built packages to run the
|
|
|
|
// smoke-tests as offline as possible.
|
|
|
|
if (Config.CustomPackagesPath != null)
|
|
|
|
{
|
|
|
|
if (!Directory.Exists(Config.CustomPackagesPath))
|
|
|
|
{
|
|
|
|
throw new ArgumentException($"Specified --with-packages {Config.CustomPackagesPath} does not exist.");
|
|
|
|
}
|
|
|
|
nugetConfig = nugetConfig.Replace("CUSTOM_PACKAGE_FEED", Config.CustomPackagesPath);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nugetConfig = string.Join(Environment.NewLine, nugetConfig.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Where(s => !s.Contains("CUSTOM_PACKAGE_FEED")).ToArray());
|
|
|
|
}
|
2022-03-03 08:02:48 -06:00
|
|
|
File.WriteAllText(nugetConfigPath, nugetConfig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-13 06:05:35 -07:00
|
|
|
public void ExecuteCmd(string args, string? workingDirectory = null, Action<Process>? additionalProcessConfigCallback = null, int? expectedExitCode = 0, int millisecondTimeout = -1)
|
2022-03-03 08:02:48 -06:00
|
|
|
{
|
|
|
|
(Process Process, string StdOut, string StdErr) executeResult = ExecuteHelper.ExecuteProcess(
|
|
|
|
DotNetPath,
|
|
|
|
args,
|
|
|
|
OutputHelper,
|
2022-03-15 11:00:38 -07:00
|
|
|
configure: (process) => configureProcess(process, workingDirectory),
|
|
|
|
millisecondTimeout: millisecondTimeout);
|
|
|
|
|
2022-10-13 06:05:35 -07:00
|
|
|
if (expectedExitCode != null) {
|
|
|
|
ExecuteHelper.ValidateExitCode(executeResult, (int) expectedExitCode);
|
|
|
|
}
|
2022-04-15 15:24:48 -05:00
|
|
|
|
|
|
|
void configureProcess(Process process, string? workingDirectory)
|
|
|
|
{
|
|
|
|
ConfigureProcess(process, workingDirectory);
|
|
|
|
|
|
|
|
additionalProcessConfigCallback?.Invoke(process);
|
|
|
|
}
|
2022-01-21 07:59:36 -06:00
|
|
|
}
|
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
public static void ConfigureProcess(Process process, string? workingDirectory, bool setPath = false)
|
2022-01-21 07:59:36 -06:00
|
|
|
{
|
2022-03-03 08:02:48 -06:00
|
|
|
if (workingDirectory != null)
|
|
|
|
{
|
|
|
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
|
|
|
}
|
|
|
|
|
|
|
|
process.StartInfo.EnvironmentVariables["DOTNET_CLI_TELEMETRY_OPTOUT"] = "1";
|
|
|
|
process.StartInfo.EnvironmentVariables["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1";
|
|
|
|
process.StartInfo.EnvironmentVariables["DOTNET_ROOT"] = Config.DotNetDirectory;
|
|
|
|
process.StartInfo.EnvironmentVariables["NUGET_PACKAGES"] = PackagesDirectory;
|
2022-01-21 07:59:36 -06:00
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
if (setPath)
|
|
|
|
{
|
|
|
|
process.StartInfo.EnvironmentVariables["PATH"] = $"{Config.DotNetDirectory}:{Environment.GetEnvironmentVariable("PATH")}";
|
|
|
|
}
|
2022-01-21 07:59:36 -06:00
|
|
|
}
|
2022-02-18 06:51:43 -08:00
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
public void ExecuteBuild(string projectName) =>
|
|
|
|
ExecuteCmd($"build {GetBinLogOption(projectName, "build")}", GetProjectDirectory(projectName));
|
|
|
|
|
2022-02-18 06:51:43 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Create a new .NET project and return the path to the created project folder.
|
|
|
|
/// </summary>
|
2022-03-03 08:02:48 -06:00
|
|
|
public string ExecuteNew(string projectType, string name, string? language = null, string? customArgs = null)
|
|
|
|
{
|
|
|
|
string projectDirectory = GetProjectDirectory(name);
|
|
|
|
string options = $"--name {name} --output {projectDirectory}";
|
|
|
|
if (language != null)
|
|
|
|
{
|
|
|
|
options += $" --language \"{language}\"";
|
|
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(customArgs))
|
|
|
|
{
|
|
|
|
options += $" {customArgs}";
|
|
|
|
}
|
|
|
|
|
|
|
|
ExecuteCmd($"new {projectType} {options}");
|
|
|
|
|
|
|
|
return projectDirectory;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void ExecutePublish(string projectName, bool? selfContained = null, string? rid = null, bool trimmed = false, bool readyToRun = false)
|
2022-02-18 06:51:43 -08:00
|
|
|
{
|
2022-03-03 08:02:48 -06:00
|
|
|
string options = string.Empty;
|
|
|
|
string binlogDifferentiator = string.Empty;
|
2022-02-18 06:51:43 -08:00
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
if (selfContained.HasValue)
|
|
|
|
{
|
|
|
|
options += $"--self-contained {selfContained.Value.ToString().ToLowerInvariant()}";
|
|
|
|
if (selfContained.Value)
|
|
|
|
{
|
|
|
|
binlogDifferentiator += "self-contained";
|
|
|
|
if (!string.IsNullOrEmpty(rid))
|
|
|
|
{
|
|
|
|
options += $" -r {rid}";
|
|
|
|
binlogDifferentiator += $"-{rid}";
|
|
|
|
}
|
|
|
|
if (trimmed)
|
|
|
|
{
|
|
|
|
options += " /p:PublishTrimmed=true";
|
|
|
|
binlogDifferentiator += "-trimmed";
|
|
|
|
}
|
|
|
|
if (readyToRun)
|
|
|
|
{
|
|
|
|
options += " /p:PublishReadyToRun=true";
|
|
|
|
binlogDifferentiator += "-R2R";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-18 06:51:43 -08:00
|
|
|
|
2022-03-03 08:02:48 -06:00
|
|
|
ExecuteCmd(
|
|
|
|
$"publish {options} {GetBinLogOption(projectName, "publish", binlogDifferentiator)}",
|
|
|
|
GetProjectDirectory(projectName));
|
2022-02-18 06:51:43 -08:00
|
|
|
}
|
2022-03-03 08:02:48 -06:00
|
|
|
|
|
|
|
public void ExecuteRun(string projectName) =>
|
|
|
|
ExecuteCmd($"run {GetBinLogOption(projectName, "run")}", GetProjectDirectory(projectName));
|
|
|
|
|
|
|
|
public void ExecuteRunWeb(string projectName)
|
|
|
|
{
|
2022-03-15 11:00:38 -07:00
|
|
|
ExecuteCmd(
|
2022-03-03 08:02:48 -06:00
|
|
|
$"run {GetBinLogOption(projectName, "run")}",
|
2022-03-15 11:00:38 -07:00
|
|
|
GetProjectDirectory(projectName),
|
|
|
|
additionalProcessConfigCallback: processConfigCallback,
|
2022-03-03 08:02:48 -06:00
|
|
|
millisecondTimeout: 30000);
|
|
|
|
|
2022-03-15 11:00:38 -07:00
|
|
|
void processConfigCallback(Process process)
|
2022-03-03 08:02:48 -06:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void ExecuteTest(string projectName) =>
|
|
|
|
ExecuteCmd($"test {GetBinLogOption(projectName, "test")}", GetProjectDirectory(projectName));
|
|
|
|
|
|
|
|
private static string GetBinLogOption(string projectName, string command, string? differentiator = null)
|
|
|
|
{
|
|
|
|
string fileName = $"{projectName}-{command}";
|
|
|
|
if (!string.IsNullOrEmpty(differentiator))
|
|
|
|
{
|
|
|
|
fileName += $"-{differentiator}";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $"/bl:{Path.Combine(LogsDirectory, $"{fileName}.binlog")}";
|
|
|
|
}
|
|
|
|
|
|
|
|
private static string GetProjectDirectory(string projectName) => Path.Combine(ProjectsDirectory, projectName);
|
2022-01-21 07:59:36 -06:00
|
|
|
}
|