|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Server.IntegrationTesting;
public class TestMatrix : IEnumerable<object[]>
{
public IList<ServerType> Servers { get; set; } = new List<ServerType>();
public IList<string> Tfms { get; set; } = new List<string>();
public IList<ApplicationType> ApplicationTypes { get; set; } = new List<ApplicationType>();
public IList<RuntimeArchitecture> Architectures { get; set; } = new List<RuntimeArchitecture>();
// ANCM specific...
public IList<HostingModel> HostingModels { get; set; } = new List<HostingModel>();
private IList<Tuple<Func<TestVariant, bool>, string>> Skips { get; } = new List<Tuple<Func<TestVariant, bool>, string>>();
public static TestMatrix ForServers(params ServerType[] types)
{
return new TestMatrix()
{
Servers = types
};
}
public TestMatrix WithTfms(params string[] tfms)
{
Tfms = tfms;
return this;
}
public TestMatrix WithApplicationTypes(params ApplicationType[] types)
{
ApplicationTypes = types;
return this;
}
public TestMatrix WithAllApplicationTypes()
{
ApplicationTypes.Add(ApplicationType.Portable);
ApplicationTypes.Add(ApplicationType.Standalone);
return this;
}
public TestMatrix WithArchitectures(params RuntimeArchitecture[] archs)
{
Architectures = archs;
return this;
}
/// <summary>
/// With all architectures that are compatible with the currently running architecture
/// </summary>
/// <returns></returns>
public TestMatrix WithAllArchitectures()
{
Architectures.Add(RuntimeArchitectures.Current);
if (RuntimeInformation.OSArchitecture == Architecture.X64)
{
Architectures.Add(RuntimeArchitecture.x86);
}
return this;
}
public TestMatrix WithHostingModels(params HostingModel[] models)
{
HostingModels = models;
return this;
}
public TestMatrix WithAllHostingModels()
{
HostingModels.Add(HostingModel.OutOfProcess);
HostingModels.Add(HostingModel.InProcess);
return this;
}
/// <summary>
/// V2 + InProc
/// </summary>
/// <returns></returns>
public TestMatrix WithAncmV2InProcess() => WithHostingModels(HostingModel.InProcess);
public TestMatrix Skip(string message, Func<TestVariant, bool> check)
{
Skips.Add(new Tuple<Func<TestVariant, bool>, string>(check, message));
return this;
}
private IEnumerable<TestVariant> Build()
{
if (!Servers.Any())
{
throw new ArgumentException("No servers were specified.");
}
// TFMs.
if (!Tfms.Any())
{
throw new ArgumentException("No TFMs were specified.");
}
ResolveDefaultArchitecture();
if (!ApplicationTypes.Any())
{
ApplicationTypes.Add(ApplicationType.Portable);
}
if (!HostingModels.Any())
{
HostingModels.Add(HostingModel.OutOfProcess);
}
var variants = new List<TestVariant>();
VaryByServer(variants);
CheckForSkips(variants);
return variants;
}
private void ResolveDefaultArchitecture()
{
if (!Architectures.Any())
{
Architectures.Add(RuntimeArchitectures.Current);
}
}
private void VaryByServer(List<TestVariant> variants)
{
foreach (var server in Servers)
{
var skip = SkipIfServerIsNotSupportedOnThisOS(server);
VaryByTfm(variants, server, skip);
}
}
private static string SkipIfServerIsNotSupportedOnThisOS(ServerType server)
{
var skip = false;
switch (server)
{
case ServerType.IIS:
case ServerType.IISExpress:
case ServerType.HttpSys:
skip = !OperatingSystem.IsWindows();
break;
case ServerType.Kestrel:
break;
case ServerType.Nginx:
// Technically it's possible but we don't test it.
skip = OperatingSystem.IsWindows();
break;
default:
throw new ArgumentException(server.ToString());
}
return skip ? "This server is not supported on this operating system." : null;
}
private void VaryByTfm(List<TestVariant> variants, ServerType server, string skip)
{
foreach (var tfm in Tfms)
{
if (!CheckTfmIsSupportedForServer(tfm, server))
{
// Don't generate net462 variations for nginx server.
continue;
}
var skipTfm = skip ?? SkipIfTfmIsNotSupportedOnThisOS(tfm);
VaryByApplicationType(variants, server, tfm, skipTfm);
}
}
private static bool CheckTfmIsSupportedForServer(string tfm, ServerType server)
{
// Not a combination we test
return !(Tfm.Matches(Tfm.Net462, tfm) && ServerType.Nginx == server);
}
private static string SkipIfTfmIsNotSupportedOnThisOS(string tfm)
{
if (Tfm.Matches(Tfm.Net462, tfm) && !OperatingSystem.IsWindows())
{
return "This TFM is not supported on this operating system.";
}
return null;
}
private void VaryByApplicationType(List<TestVariant> variants, ServerType server, string tfm, string skip)
{
foreach (var t in ApplicationTypes)
{
var type = t;
if (Tfm.Matches(Tfm.Net462, tfm) && type == ApplicationType.Portable)
{
if (ApplicationTypes.Count == 1)
{
// Override the default
type = ApplicationType.Standalone;
}
else
{
continue;
}
}
VaryByArchitecture(variants, server, tfm, skip, type);
}
}
private void VaryByArchitecture(List<TestVariant> variants, ServerType server, string tfm, string skip, ApplicationType type)
{
foreach (var arch in Architectures)
{
if (!IsArchitectureSupportedOnServer(arch, server))
{
continue;
}
var archSkip = skip ?? SkipIfArchitectureNotSupportedOnCurrentSystem(arch);
if (server == ServerType.IISExpress || server == ServerType.IIS)
{
VaryByAncmHostingModel(variants, server, tfm, type, arch, archSkip);
}
else
{
variants.Add(new TestVariant()
{
Server = server,
Tfm = tfm,
ApplicationType = type,
Architecture = arch,
Skip = archSkip,
});
}
}
}
private static string SkipIfArchitectureNotSupportedOnCurrentSystem(RuntimeArchitecture arch)
{
if (arch == RuntimeArchitecture.x64)
{
// Can't run x64 on a x86 OS.
return (RuntimeInformation.OSArchitecture == Architecture.Arm || RuntimeInformation.OSArchitecture == Architecture.X86)
? $"Cannot run {arch} on your current system." : null;
}
// No x86 runtimes available on MacOS or Linux.
return OperatingSystem.IsWindows() ? null : $"No {arch} available for non-Windows systems.";
}
private static bool IsArchitectureSupportedOnServer(RuntimeArchitecture arch, ServerType server)
{
// No x86 Mac/Linux runtime, don't generate a test variation that will always be skipped.
return !(arch == RuntimeArchitecture.x86 && ServerType.Nginx == server);
}
private void VaryByAncmHostingModel(IList<TestVariant> variants, ServerType server, string tfm, ApplicationType type, RuntimeArchitecture arch, string skip)
{
foreach (var hostingModel in HostingModels)
{
var skipAncm = skip;
if (hostingModel == HostingModel.InProcess)
{
// Not supported
if (Tfm.Matches(Tfm.Net462, tfm) || Tfm.Matches(Tfm.NetCoreApp20, tfm))
{
continue;
}
if (!IISExpressAncmSchema.SupportsInProcessHosting)
{
skipAncm = skipAncm ?? IISExpressAncmSchema.SkipReason;
}
}
variants.Add(new TestVariant()
{
Server = server,
Tfm = tfm,
ApplicationType = type,
Architecture = arch,
HostingModel = hostingModel,
Skip = skipAncm,
});
}
}
private void CheckForSkips(List<TestVariant> variants)
{
foreach (var variant in variants)
{
foreach (var skipPair in Skips)
{
if (skipPair.Item1(variant))
{
variant.Skip = skipPair.Item2;
break;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<object[]>)this).GetEnumerator();
}
// This is what Xunit MemberData expects
public IEnumerator<object[]> GetEnumerator()
{
foreach (var v in Build())
{
yield return new[] { v };
}
}
}
|