186 lines
7.6 KiB
C#
186 lines
7.6 KiB
C#
using AspNetCoreSdkTests.Util;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
|
|
namespace AspNetCoreSdkTests.Templates
|
|
{
|
|
public abstract class Template
|
|
{
|
|
private static readonly TimeSpan _sleepBetweenHttpRequests = TimeSpan.FromMilliseconds(100);
|
|
private static readonly TimeSpan _sleepBetweenOutputContains = TimeSpan.FromMilliseconds(100);
|
|
|
|
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler()
|
|
{
|
|
// Allow self-signed certs
|
|
ServerCertificateCustomValidationCallback = (m, c, ch, p) => true
|
|
});
|
|
|
|
private static ConcurrentDictionary<(Type, NuGetPackageSource), Template> _templates = new ConcurrentDictionary<(Type, NuGetPackageSource), Template>();
|
|
|
|
public static T GetInstance<T>(NuGetPackageSource nuGetPackageSource) where T : Template, new()
|
|
{
|
|
return (T)_templates.GetOrAdd((typeof(T), nuGetPackageSource), (k) => new T() { NuGetPackageSource = nuGetPackageSource });
|
|
}
|
|
|
|
private Lazy<IEnumerable<string>> _objFilesAfterRestore;
|
|
private Lazy<(IEnumerable<string> ObjFiles, IEnumerable<string> BinFiles)> _filesAfterBuild;
|
|
private Lazy<IEnumerable<string>> _filesAfterPublish;
|
|
private Lazy<(HttpResponseMessage Http, HttpResponseMessage Https)> _httpResponsesAfterRun;
|
|
private Lazy<(HttpResponseMessage Http, HttpResponseMessage Https)> _httpResponsesAfterExec;
|
|
|
|
public NuGetPackageSource NuGetPackageSource { get; private set; }
|
|
|
|
protected Template()
|
|
{
|
|
_objFilesAfterRestore = new Lazy<IEnumerable<string>>(
|
|
GetObjFilesAfterRestore, LazyThreadSafetyMode.ExecutionAndPublication);
|
|
|
|
_filesAfterBuild = new Lazy<(IEnumerable<string> ObjFiles, IEnumerable<string> BinFiles)>(
|
|
GetFilesAfterBuild, LazyThreadSafetyMode.ExecutionAndPublication);
|
|
|
|
_filesAfterPublish = new Lazy<IEnumerable<string>>(
|
|
GetFilesAfterPublish, LazyThreadSafetyMode.ExecutionAndPublication);
|
|
|
|
_httpResponsesAfterRun = new Lazy<(HttpResponseMessage Http, HttpResponseMessage Https)>(
|
|
GetHttpResponsesAfterRun, LazyThreadSafetyMode.ExecutionAndPublication);
|
|
|
|
_httpResponsesAfterExec = new Lazy<(HttpResponseMessage Http, HttpResponseMessage Https)>(
|
|
GetHttpResponsesAfterExec, LazyThreadSafetyMode.ExecutionAndPublication);
|
|
}
|
|
|
|
public override string ToString() => $"{Name},{NuGetPackageSource}";
|
|
|
|
private string TempDir => Path.Combine(AssemblySetUp.TempDir, Name, NuGetPackageSource.ToString());
|
|
|
|
public abstract string Name { get; }
|
|
public abstract TemplateType Type { get; }
|
|
public virtual string RelativeUrl => string.Empty;
|
|
|
|
public IEnumerable<string> ObjFilesAfterRestore => _objFilesAfterRestore.Value;
|
|
public IEnumerable<string> ObjFilesAfterBuild => _filesAfterBuild.Value.ObjFiles;
|
|
public IEnumerable<string> BinFilesAfterBuild => _filesAfterBuild.Value.BinFiles;
|
|
public IEnumerable<string> FilesAfterPublish => _filesAfterPublish.Value;
|
|
public HttpResponseMessage HttpResponseAfterRun => _httpResponsesAfterRun.Value.Http;
|
|
public HttpResponseMessage HttpsResponseAfterRun => _httpResponsesAfterRun.Value.Https;
|
|
public HttpResponseMessage HttpResponseAfterExec => _httpResponsesAfterExec.Value.Http;
|
|
public HttpResponseMessage HttpsResponseAfterExec => _httpResponsesAfterExec.Value.Https;
|
|
|
|
public virtual IEnumerable<string> ExpectedObjFilesAfterRestore => new[]
|
|
{
|
|
$"{Name}.csproj.nuget.cache",
|
|
$"{Name}.csproj.nuget.g.props",
|
|
$"{Name}.csproj.nuget.g.targets",
|
|
"project.assets.json",
|
|
};
|
|
|
|
public virtual IEnumerable<string> ExpectedObjFilesAfterBuild => ExpectedObjFilesAfterRestore;
|
|
|
|
public abstract IEnumerable<string> ExpectedBinFilesAfterBuild { get; }
|
|
|
|
public abstract IEnumerable<string> ExpectedFilesAfterPublish { get; }
|
|
|
|
private IEnumerable<string> GetObjFilesAfterRestore()
|
|
{
|
|
Directory.CreateDirectory(TempDir);
|
|
DotNetUtil.New(Name, TempDir);
|
|
DotNetUtil.Restore(TempDir, NuGetPackageSource);
|
|
return IOUtil.GetFiles(Path.Combine(TempDir, "obj"));
|
|
}
|
|
|
|
private (IEnumerable<string> ObjFiles, IEnumerable<string> BinFiles) GetFilesAfterBuild()
|
|
{
|
|
// Build depends on Restore
|
|
_ = ObjFilesAfterRestore;
|
|
|
|
DotNetUtil.Build(TempDir);
|
|
return (IOUtil.GetFiles(Path.Combine(TempDir, "obj")), IOUtil.GetFiles(Path.Combine(TempDir, "bin")));
|
|
}
|
|
|
|
private IEnumerable<string> GetFilesAfterPublish()
|
|
{
|
|
// Publish depends on Build
|
|
_ = BinFilesAfterBuild;
|
|
|
|
DotNetUtil.Publish(TempDir);
|
|
return IOUtil.GetFiles(Path.Combine(TempDir, DotNetUtil.PublishOutput));
|
|
}
|
|
|
|
private (HttpResponseMessage Http, HttpResponseMessage Https) GetHttpResponsesAfterRun()
|
|
{
|
|
// Run depends on Build
|
|
_ = BinFilesAfterBuild;
|
|
|
|
return GetHttpResponses(DotNetUtil.Run(TempDir));
|
|
}
|
|
|
|
private (HttpResponseMessage Http, HttpResponseMessage Https) GetHttpResponsesAfterExec()
|
|
{
|
|
// Exec depends on Publish
|
|
_ = FilesAfterPublish;
|
|
|
|
return GetHttpResponses(DotNetUtil.Exec(TempDir, Name));
|
|
}
|
|
|
|
private (HttpResponseMessage Http, HttpResponseMessage Https) GetHttpResponses(
|
|
(Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) process)
|
|
{
|
|
try
|
|
{
|
|
var (httpUrl, httpsUrl) = ScrapeUrls(process);
|
|
return (GetAsync(new Uri(new Uri(httpUrl), RelativeUrl)), GetAsync(new Uri(new Uri(httpsUrl), RelativeUrl)));
|
|
}
|
|
finally
|
|
{
|
|
DotNetUtil.StopProcess(process, throwOnError: false);
|
|
}
|
|
}
|
|
|
|
private (string HttpUrl, string HttpsUrl) ScrapeUrls(
|
|
(Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) process)
|
|
{
|
|
// Extract URLs from output
|
|
while (true)
|
|
{
|
|
var output = process.OutputBuilder.ToString();
|
|
if (output.Contains("Application started"))
|
|
{
|
|
var httpUrl = Regex.Match(output, @"Now listening on: (http:\S*)").Groups[1].Value;
|
|
var httpsUrl = Regex.Match(output, @"Now listening on: (https:\S*)").Groups[1].Value;
|
|
return (httpUrl, httpsUrl);
|
|
}
|
|
else if (process.Process.HasExited)
|
|
{
|
|
var startInfo = process.Process.StartInfo;
|
|
throw new InvalidOperationException(
|
|
$"Failed to start process '{startInfo.FileName} {startInfo.Arguments}'" + Environment.NewLine + output);
|
|
}
|
|
else
|
|
{
|
|
Thread.Sleep(_sleepBetweenOutputContains);
|
|
}
|
|
}
|
|
}
|
|
|
|
private HttpResponseMessage GetAsync(Uri requestUri)
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
return _httpClient.GetAsync(requestUri).Result;
|
|
}
|
|
catch
|
|
{
|
|
Thread.Sleep(_sleepBetweenHttpRequests);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|