Refactor tests to use idempotent templates
- Benefit is fine-grained tests without perf hit of duplicate work
This commit is contained in:
parent
1e5b3a5cc5
commit
52f483bf44
|
|
@ -0,0 +1,35 @@
|
||||||
|
using AspNetCoreSdkTests.Templates;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AspNetCoreSdkTests
|
||||||
|
{
|
||||||
|
public static class TemplateData
|
||||||
|
{
|
||||||
|
public static IEnumerable<TestCaseData> Restore = new TestCaseData[]
|
||||||
|
{
|
||||||
|
new TestCaseData(Template.GetInstance<ClassLibraryTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<ConsoleApplicationTemplate>(NuGetConfig.Empty)),
|
||||||
|
|
||||||
|
// Offline restore currently not supported for RazorClassLibrary template (https://github.com/aspnet/Universe/issues/1123)
|
||||||
|
new TestCaseData(Template.GetInstance<RazorClassLibraryTemplate>(NuGetConfig.NuGetOrg)),
|
||||||
|
|
||||||
|
new TestCaseData(Template.GetInstance<WebTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<RazorTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<MvcTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<AngularTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<ReactTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<ReactReduxTemplate>(NuGetConfig.Empty)),
|
||||||
|
new TestCaseData(Template.GetInstance<WebApiTemplate>(NuGetConfig.Empty)),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IEnumerable<TestCaseData> Build => Restore;
|
||||||
|
|
||||||
|
public static IEnumerable<TestCaseData> Publish => Restore;
|
||||||
|
|
||||||
|
public static IEnumerable<TestCaseData> Run = Restore.Where(d => ((Template)d.Arguments[0]).Type == TemplateType.WebApplication);
|
||||||
|
|
||||||
|
public static IEnumerable<TestCaseData> Exec => Run;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,71 +1,47 @@
|
||||||
using AspNetCoreSdkTests.Templates;
|
using AspNetCoreSdkTests.Templates;
|
||||||
using AspNetCoreSdkTests.Util;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace AspNetCoreSdkTests
|
namespace AspNetCoreSdkTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TemplateTests
|
public class TemplateTests
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan _sleepBetweenHttpRequests = TimeSpan.FromMilliseconds(100);
|
|
||||||
|
|
||||||
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler()
|
|
||||||
{
|
|
||||||
// Allow self-signed certs
|
|
||||||
ServerCertificateCustomValidationCallback = (m, c, ch, p) => true
|
|
||||||
});
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[TestCaseSource(typeof(TemplateData), nameof(TemplateData.Current))]
|
[TestCaseSource(typeof(TemplateData), nameof(TemplateData.Restore))]
|
||||||
public void RestoreBuildRunPublish(Template template, NuGetConfig nuGetConfig)
|
public void Restore(Template template)
|
||||||
{
|
{
|
||||||
using (var context = new DotNetContext(template))
|
CollectionAssert.AreEquivalent(template.ExpectedObjFilesAfterRestore, template.ObjFilesAfterRestore);
|
||||||
{
|
|
||||||
context.New();
|
|
||||||
|
|
||||||
context.Restore(nuGetConfig);
|
|
||||||
CollectionAssert.AreEquivalent(template.ExpectedObjFilesAfterRestore, context.GetObjFiles());
|
|
||||||
|
|
||||||
context.Build();
|
|
||||||
CollectionAssert.AreEquivalent(template.ExpectedObjFilesAfterBuild, context.GetObjFiles());
|
|
||||||
CollectionAssert.AreEquivalent(template.ExpectedBinFilesAfterBuild, context.GetBinFiles());
|
|
||||||
|
|
||||||
if (template.Type == TemplateType.WebApplication)
|
|
||||||
{
|
|
||||||
var (httpUrl, httpsUrl) = context.Run();
|
|
||||||
Assert.AreEqual(HttpStatusCode.OK, GetAsync(new Uri(new Uri(httpUrl), template.RelativeUrl)).StatusCode);
|
|
||||||
Assert.AreEqual(HttpStatusCode.OK, GetAsync(new Uri(new Uri(httpsUrl), template.RelativeUrl)).StatusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Publish();
|
|
||||||
CollectionAssert.AreEquivalent(template.ExpectedFilesAfterPublish, context.GetPublishFiles());
|
|
||||||
|
|
||||||
if (template.Type == TemplateType.WebApplication)
|
|
||||||
{
|
|
||||||
var (httpUrl, httpsUrl) = context.Exec();
|
|
||||||
Assert.AreEqual(HttpStatusCode.OK, GetAsync(new Uri(new Uri(httpUrl), template.RelativeUrl)).StatusCode);
|
|
||||||
Assert.AreEqual(HttpStatusCode.OK, GetAsync(new Uri(new Uri(httpsUrl), template.RelativeUrl)).StatusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpResponseMessage GetAsync(Uri requestUri)
|
[Test]
|
||||||
|
[TestCaseSource(typeof(TemplateData), nameof(TemplateData.Build))]
|
||||||
|
public void Build(Template template)
|
||||||
{
|
{
|
||||||
while (true)
|
CollectionAssert.AreEquivalent(template.ExpectedObjFilesAfterBuild, template.ObjFilesAfterBuild);
|
||||||
{
|
}
|
||||||
try
|
|
||||||
{
|
[Test]
|
||||||
return _httpClient.GetAsync(requestUri).Result;
|
[TestCaseSource(typeof(TemplateData), nameof(TemplateData.Publish))]
|
||||||
}
|
public void Publish(Template template)
|
||||||
catch
|
{
|
||||||
{
|
CollectionAssert.AreEquivalent(template.ExpectedFilesAfterPublish, template.FilesAfterPublish);
|
||||||
Thread.Sleep(_sleepBetweenHttpRequests);
|
}
|
||||||
}
|
|
||||||
}
|
[Test]
|
||||||
|
[TestCaseSource(typeof(TemplateData), nameof(TemplateData.Run))]
|
||||||
|
public void Run(Template template)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(HttpStatusCode.OK, template.HttpResponseAfterRun.StatusCode);
|
||||||
|
Assert.AreEqual(HttpStatusCode.OK, template.HttpsResponseAfterRun.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCaseSource(typeof(TemplateData), nameof(TemplateData.Exec))]
|
||||||
|
public void Exec(Template template)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(HttpStatusCode.OK, template.HttpResponseAfterExec.StatusCode);
|
||||||
|
Assert.AreEqual(HttpStatusCode.OK, template.HttpsResponseAfterExec.StatusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class AngularTemplate : SpaBaseTemplate
|
public class AngularTemplate : SpaBaseTemplate
|
||||||
{
|
{
|
||||||
public new static AngularTemplate Instance { get; } = new AngularTemplate();
|
public AngularTemplate() { }
|
||||||
|
|
||||||
protected AngularTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "angular";
|
public override string Name => "angular";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class ClassLibraryTemplate : Template
|
public class ClassLibraryTemplate : Template
|
||||||
{
|
{
|
||||||
public static ClassLibraryTemplate Instance { get; } = new ClassLibraryTemplate();
|
public ClassLibraryTemplate() { }
|
||||||
|
|
||||||
protected ClassLibraryTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "classlib";
|
public override string Name => "classlib";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class ConsoleApplicationTemplate : ClassLibraryTemplate
|
public class ConsoleApplicationTemplate : ClassLibraryTemplate
|
||||||
{
|
{
|
||||||
public new static ConsoleApplicationTemplate Instance { get; } = new ConsoleApplicationTemplate();
|
public ConsoleApplicationTemplate() { }
|
||||||
|
|
||||||
protected ConsoleApplicationTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "console";
|
public override string Name => "console";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class MvcTemplate : RazorBootstrapJQueryTemplate
|
public class MvcTemplate : RazorBootstrapJQueryTemplate
|
||||||
{
|
{
|
||||||
public new static MvcTemplate Instance { get; } = new MvcTemplate();
|
public MvcTemplate() { }
|
||||||
|
|
||||||
protected MvcTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "mvc";
|
public override string Name => "mvc";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class RazorClassLibraryTemplate : RazorBaseTemplate
|
public class RazorClassLibraryTemplate : RazorBaseTemplate
|
||||||
{
|
{
|
||||||
public new static RazorClassLibraryTemplate Instance { get; } = new RazorClassLibraryTemplate();
|
public RazorClassLibraryTemplate() { }
|
||||||
|
|
||||||
protected RazorClassLibraryTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "razorclasslib";
|
public override string Name => "razorclasslib";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class RazorTemplate : RazorBootstrapJQueryTemplate
|
public class RazorTemplate : RazorBootstrapJQueryTemplate
|
||||||
{
|
{
|
||||||
public new static RazorTemplate Instance { get; } = new RazorTemplate();
|
public RazorTemplate() { }
|
||||||
|
|
||||||
protected RazorTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "razor";
|
public override string Name => "razor";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class ReactReduxTemplate : ReactTemplate
|
public class ReactReduxTemplate : ReactTemplate
|
||||||
{
|
{
|
||||||
public new static ReactReduxTemplate Instance { get; } = new ReactReduxTemplate();
|
public ReactReduxTemplate() { }
|
||||||
|
|
||||||
protected ReactReduxTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "reactredux";
|
public override string Name => "reactredux";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class ReactTemplate : SpaBaseTemplate
|
public class ReactTemplate : SpaBaseTemplate
|
||||||
{
|
{
|
||||||
public new static ReactTemplate Instance { get; } = new ReactTemplate();
|
public ReactTemplate() { }
|
||||||
|
|
||||||
protected ReactTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "react";
|
public override string Name => "react";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,76 @@
|
||||||
using System.Collections.Generic;
|
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
|
namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public abstract class Template
|
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, NuGetConfig), Template> _templates = new ConcurrentDictionary<(Type, NuGetConfig), Template>();
|
||||||
|
|
||||||
|
public static T GetInstance<T>(NuGetConfig nuGetConfig) where T : Template, new()
|
||||||
|
{
|
||||||
|
return (T)_templates.GetOrAdd((typeof(T), nuGetConfig), (k) => new T() { NuGetConfig = nuGetConfig });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 NuGetConfig NuGetConfig { 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},{NuGetConfig}";
|
||||||
|
|
||||||
|
private string TempDir => Path.Combine(AssemblySetUp.TempDir, Name, NuGetConfig.ToString());
|
||||||
|
|
||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
||||||
public abstract TemplateType Type { get; }
|
public abstract TemplateType Type { get; }
|
||||||
public virtual string RelativeUrl => string.Empty;
|
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[]
|
public virtual IEnumerable<string> ExpectedObjFilesAfterRestore => new[]
|
||||||
{
|
{
|
||||||
$"{Name}.csproj.nuget.cache",
|
$"{Name}.csproj.nuget.cache",
|
||||||
|
|
@ -22,6 +85,101 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
|
|
||||||
public abstract IEnumerable<string> ExpectedFilesAfterPublish { get; }
|
public abstract IEnumerable<string> ExpectedFilesAfterPublish { get; }
|
||||||
|
|
||||||
public override string ToString() => Name;
|
private IEnumerable<string> GetObjFilesAfterRestore()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(TempDir);
|
||||||
|
DotNetUtil.New(Name, TempDir);
|
||||||
|
DotNetUtil.Restore(TempDir, NuGetConfig);
|
||||||
|
return IOUtil.GetFiles(System.IO.Path.Combine(TempDir, "obj"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private (IEnumerable<string> ObjFiles, IEnumerable<string> BinFiles) GetFilesAfterBuild()
|
||||||
|
{
|
||||||
|
// Build depends on Restore
|
||||||
|
_ = ObjFilesAfterRestore;
|
||||||
|
|
||||||
|
DotNetUtil.Build(TempDir);
|
||||||
|
return (IOUtil.GetFiles(System.IO.Path.Combine(TempDir, "obj")), IOUtil.GetFiles(System.IO.Path.Combine(TempDir, "bin")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> GetFilesAfterPublish()
|
||||||
|
{
|
||||||
|
// Publish depends on Build
|
||||||
|
_ = BinFilesAfterBuild;
|
||||||
|
|
||||||
|
DotNetUtil.Publish(TempDir);
|
||||||
|
return IOUtil.GetFiles(System.IO.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
using NUnit.Framework;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace AspNetCoreSdkTests.Templates
|
|
||||||
{
|
|
||||||
public static class TemplateData
|
|
||||||
{
|
|
||||||
private static IEnumerable<Template> Templates { get; } = new Template[]
|
|
||||||
{
|
|
||||||
ConsoleApplicationTemplate.Instance,
|
|
||||||
ClassLibraryTemplate.Instance,
|
|
||||||
WebTemplate.Instance,
|
|
||||||
MvcTemplate.Instance,
|
|
||||||
RazorTemplate.Instance,
|
|
||||||
AngularTemplate.Instance,
|
|
||||||
ReactTemplate.Instance,
|
|
||||||
ReactReduxTemplate.Instance,
|
|
||||||
RazorClassLibraryTemplate.Instance,
|
|
||||||
WebApiTemplate.Instance,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static IEnumerable<NuGetConfig> NuGetConfigs { get; } = Enum.GetValues(typeof(NuGetConfig)).Cast<NuGetConfig>();
|
|
||||||
|
|
||||||
private static IEnumerable<TestCaseData> All { get; } =
|
|
||||||
from t in Templates
|
|
||||||
from c in NuGetConfigs
|
|
||||||
// Exclude the DotNetCore NuGet.config scenarios unless temporarily required to make tests pass
|
|
||||||
where c != NuGetConfig.DotNetCore
|
|
||||||
select new TestCaseData(t, c);
|
|
||||||
|
|
||||||
private static IEnumerable<TestCaseData> IgnoreRazorClassLibEmpty { get; } =
|
|
||||||
from d in All
|
|
||||||
select (
|
|
||||||
((Template)d.Arguments[0] == RazorClassLibraryTemplate.Instance && (NuGetConfig)d.Arguments[1] == NuGetConfig.Empty) ?
|
|
||||||
d.Ignore("https://github.com/aspnet/Universe/issues/1123") :
|
|
||||||
d);
|
|
||||||
|
|
||||||
public static IEnumerable<TestCaseData> Current => IgnoreRazorClassLibEmpty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,9 +5,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class WebApiTemplate : WebTemplate
|
public class WebApiTemplate : WebTemplate
|
||||||
{
|
{
|
||||||
public new static WebApiTemplate Instance { get; } = new WebApiTemplate();
|
public WebApiTemplate() { }
|
||||||
|
|
||||||
protected WebApiTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "webapi";
|
public override string Name => "webapi";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ namespace AspNetCoreSdkTests.Templates
|
||||||
{
|
{
|
||||||
public class WebTemplate : ConsoleApplicationTemplate
|
public class WebTemplate : ConsoleApplicationTemplate
|
||||||
{
|
{
|
||||||
public new static WebTemplate Instance { get; } = new WebTemplate();
|
public WebTemplate() { }
|
||||||
|
|
||||||
protected WebTemplate() { }
|
|
||||||
|
|
||||||
public override string Name => "web";
|
public override string Name => "web";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,23 @@
|
||||||
using NUnit.Framework;
|
using AspNetCoreSdkTests.Util;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
// Run all test cases in parallel
|
// Run all test cases in parallel
|
||||||
[assembly: Parallelizable(ParallelScope.Children)]
|
[assembly: Parallelizable(ParallelScope.Children)]
|
||||||
|
|
||||||
|
[SetUpFixture]
|
||||||
|
public class AssemblySetUp
|
||||||
|
{
|
||||||
|
public static string TempDir { get; private set; }
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
TempDir = IOUtil.GetTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public void TearDown()
|
||||||
|
{
|
||||||
|
IOUtil.DeleteDir(TempDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
using AspNetCoreSdkTests.Templates;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace AspNetCoreSdkTests.Util
|
|
||||||
{
|
|
||||||
public class DotNetContext : TempDir
|
|
||||||
{
|
|
||||||
private static readonly TimeSpan _sleepBetweenOutputContains = TimeSpan.FromMilliseconds(100);
|
|
||||||
|
|
||||||
private (Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) _runProcess;
|
|
||||||
private (Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) _execProcess;
|
|
||||||
|
|
||||||
public DotNetContext(Template template) { Template = template; }
|
|
||||||
|
|
||||||
public Template Template { get; }
|
|
||||||
|
|
||||||
public string New()
|
|
||||||
{
|
|
||||||
return DotNetUtil.New(Template.Name, Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Restore(NuGetConfig config)
|
|
||||||
{
|
|
||||||
return DotNetUtil.Restore(Path, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Build()
|
|
||||||
{
|
|
||||||
return DotNetUtil.Build(Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public (string httpUrl, string httpsUrl) Run()
|
|
||||||
{
|
|
||||||
_runProcess = DotNetUtil.Run(Path);
|
|
||||||
return ScrapeUrls(_runProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
public (string httpUrl, string httpsUrl) Exec()
|
|
||||||
{
|
|
||||||
_execProcess = DotNetUtil.Exec(Path, Template.Name);
|
|
||||||
return ScrapeUrls(_execProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Publish()
|
|
||||||
{
|
|
||||||
return DotNetUtil.Publish(Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<string> GetObjFiles()
|
|
||||||
{
|
|
||||||
return IOUtil.GetFiles(System.IO.Path.Combine(Path, "obj"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<string> GetBinFiles()
|
|
||||||
{
|
|
||||||
return IOUtil.GetFiles(System.IO.Path.Combine(Path, "bin"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<string> GetPublishFiles()
|
|
||||||
{
|
|
||||||
return IOUtil.GetFiles(System.IO.Path.Combine(Path, DotNetUtil.PublishOutput));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispose()
|
|
||||||
{
|
|
||||||
// Must stop processes to release filehandles before calling base.Dispose() which deletes app dir
|
|
||||||
Dispose(_runProcess);
|
|
||||||
Dispose(_execProcess);
|
|
||||||
|
|
||||||
base.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Dispose((Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) process)
|
|
||||||
{
|
|
||||||
if (process.Process != null)
|
|
||||||
{
|
|
||||||
DotNetUtil.StopProcess(process.Process, process.OutputBuilder, process.ErrorBuilder, throwOnError: false);
|
|
||||||
process.Process = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -57,7 +57,7 @@ namespace AspNetCoreSdkTests.Util
|
||||||
IEnumerable<KeyValuePair<string, string>> environment = null, bool throwOnError = true)
|
IEnumerable<KeyValuePair<string, string>> environment = null, bool throwOnError = true)
|
||||||
{
|
{
|
||||||
var p = StartDotNet(arguments, workingDirectory, environment);
|
var p = StartDotNet(arguments, workingDirectory, environment);
|
||||||
return WaitForExit(p.Process, p.OutputBuilder, p.ErrorBuilder, throwOnError: throwOnError);
|
return WaitForExit(p, throwOnError: throwOnError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) StartDotNet(
|
private static (Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) StartDotNet(
|
||||||
|
|
@ -110,38 +110,38 @@ namespace AspNetCoreSdkTests.Util
|
||||||
return (process, outputBuilder, errorBuilder);
|
return (process, outputBuilder, errorBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string StopProcess(Process process, ConcurrentStringBuilder outputBuilder, ConcurrentStringBuilder errorBuilder,
|
public static string StopProcess((Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) process,
|
||||||
bool throwOnError = true)
|
bool throwOnError = true)
|
||||||
{
|
{
|
||||||
if (!process.HasExited)
|
if (!process.Process.HasExited)
|
||||||
{
|
{
|
||||||
process.KillTree();
|
process.Process.KillTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
return WaitForExit(process, outputBuilder, errorBuilder, throwOnError: throwOnError);
|
return WaitForExit(process, throwOnError: throwOnError);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string WaitForExit(Process process, ConcurrentStringBuilder outputBuilder, ConcurrentStringBuilder errorBuilder,
|
public static string WaitForExit((Process Process, ConcurrentStringBuilder OutputBuilder, ConcurrentStringBuilder ErrorBuilder) process,
|
||||||
bool throwOnError = true)
|
bool throwOnError = true)
|
||||||
{
|
{
|
||||||
// Workaround issue where WaitForExit() blocks until child processes are killed, which is problematic
|
// Workaround issue where WaitForExit() blocks until child processes are killed, which is problematic
|
||||||
// for the dotnet.exe NodeReuse child processes. I'm not sure why this is problematic for dotnet.exe child processes
|
// for the dotnet.exe NodeReuse child processes. I'm not sure why this is problematic for dotnet.exe child processes
|
||||||
// but not for MSBuild.exe child processes. The workaround is to specify a large timeout.
|
// but not for MSBuild.exe child processes. The workaround is to specify a large timeout.
|
||||||
// https://stackoverflow.com/a/37983587/102052
|
// https://stackoverflow.com/a/37983587/102052
|
||||||
process.WaitForExit(int.MaxValue);
|
process.Process.WaitForExit(int.MaxValue);
|
||||||
|
|
||||||
if (throwOnError && process.ExitCode != 0)
|
if (throwOnError && process.Process.ExitCode != 0)
|
||||||
{
|
{
|
||||||
var sb = new ConcurrentStringBuilder();
|
var sb = new ConcurrentStringBuilder();
|
||||||
|
|
||||||
sb.AppendLine($"Command {process.StartInfo.FileName} {process.StartInfo.Arguments} returned exit code {process.ExitCode}");
|
sb.AppendLine($"Command {process.Process.StartInfo.FileName} {process.Process.StartInfo.Arguments} returned exit code {process.Process.ExitCode}");
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine(outputBuilder.ToString());
|
sb.AppendLine(process.OutputBuilder.ToString());
|
||||||
|
|
||||||
throw new InvalidOperationException(sb.ToString());
|
throw new InvalidOperationException(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputBuilder.ToString();
|
return process.OutputBuilder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace AspNetCoreSdkTests.Util
|
|
||||||
{
|
|
||||||
public class TempDir : IDisposable
|
|
||||||
{
|
|
||||||
public string Path { get; }
|
|
||||||
|
|
||||||
public TempDir()
|
|
||||||
{
|
|
||||||
Path = IOUtil.GetTempDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
IOUtil.DeleteDir(Path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue