|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Xml;
namespace Microsoft.NET.TestFramework
{
public class TestCommandLine
{
public List<string>? RemainingArgs { get; private set; }
public bool UseFullFrameworkMSBuild { get; private set; }
public string? FullFrameworkMSBuildPath { get; private set; }
public string? DotnetHostPath { get; private set; }
public string? SDKRepoPath { get; private set; }
public string? SDKRepoConfiguration { get; private set; }
public bool NoRepoInference { get; private set; }
public bool ShouldShowHelp { get; private set; }
public string? SdkVersion { get; private set; }
public string? TestExecutionDirectory { get; set; }
public string? MsbuildAdditionalSdkResolverFolder { get; set; }
public List<(string name, string value)> EnvironmentVariables { get; set; } = [];
public List<string> TestConfigFiles { get; private set; } = [];
public HashSet<string> TestListsToRun { get; private set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public bool ShowSdkInfo { get; private set; }
public static TestCommandLine Parse(string[] args)
{
TestCommandLine ret = new()
{
RemainingArgs = new List<string>()
};
Stack<string> argStack = new(args.Reverse());
while (argStack.Any())
{
string arg = argStack.Pop();
if (arg.Equals("-useFullMSBuild", StringComparison.InvariantCultureIgnoreCase))
{
ret.UseFullFrameworkMSBuild = true;
}
else if (arg.Equals("-fullMSBuildPath", StringComparison.InvariantCultureIgnoreCase) && argStack.Any())
{
ret.FullFrameworkMSBuildPath = argStack.Pop();
}
else if (arg.Equals("-dotnetPath", StringComparison.InvariantCultureIgnoreCase) && argStack.Any())
{
ret.DotnetHostPath = argStack.Pop();
}
else if (arg.Equals("-sdkRepo", StringComparison.InvariantCultureIgnoreCase) && argStack.Any())
{
ret.SDKRepoPath = argStack.Pop();
}
else if (arg.Equals("-sdkConfig", StringComparison.InvariantCultureIgnoreCase) && argStack.Any())
{
ret.SDKRepoConfiguration = argStack.Pop();
}
else if (arg.Equals("-noRepoInference", StringComparison.CurrentCultureIgnoreCase))
{
ret.NoRepoInference = true;
}
else if (arg.Equals("-sdkVersion", StringComparison.CurrentCultureIgnoreCase))
{
ret.SdkVersion = argStack.Pop();
}
else if (arg.Equals("-testExecutionDirectory", StringComparison.CurrentCultureIgnoreCase))
{
ret.TestExecutionDirectory = argStack.Pop();
}
else if (arg.Equals("-msbuildAdditionalSdkResolverFolder", StringComparison.CurrentCultureIgnoreCase))
{
ret.MsbuildAdditionalSdkResolverFolder = argStack.Pop();
}
else if (arg.Equals("-testConfigFile", StringComparison.CurrentCultureIgnoreCase) ||
arg.Equals("-testConfig", StringComparison.CurrentCultureIgnoreCase))
{
ret.TestConfigFiles.Add(argStack.Pop());
}
else if (arg.Equals("-testList", StringComparison.CurrentCultureIgnoreCase))
{
ret.TestListsToRun.Add(argStack.Pop());
}
else if (arg.Equals("-e", StringComparison.CurrentCultureIgnoreCase))
{
ret.EnvironmentVariables.Add(ParseEnvironmentVariableArg(argStack.Pop()));
}
else if (arg.Equals("-showSdkInfo", StringComparison.CurrentCultureIgnoreCase))
{
ret.ShowSdkInfo = true;
}
else if (arg.Equals("-help", StringComparison.CurrentCultureIgnoreCase) ||
arg.Equals("--help", StringComparison.CurrentCultureIgnoreCase) ||
arg.Equals("/?") || arg.Equals("-?"))
{
ret.ShouldShowHelp = true;
}
else
{
ret.RemainingArgs.Add(arg);
}
}
if (!string.IsNullOrEmpty(ret.SDKRepoPath) && string.IsNullOrEmpty(ret.SDKRepoConfiguration))
{
ret.SDKRepoConfiguration = "Release";
}
if (string.IsNullOrEmpty(ret.FullFrameworkMSBuildPath))
{
// Run tests on full framework MSBuild if environment variable is set pointing to it
string? msbuildPath = Environment.GetEnvironmentVariable("DOTNET_SDK_TEST_MSBUILD_PATH");
if (!string.IsNullOrEmpty(msbuildPath))
{
ret.FullFrameworkMSBuildPath = msbuildPath;
}
}
return ret;
}
private static (string name, string value) ParseEnvironmentVariableArg(string arg)
{
var i = arg.IndexOf('=');
if (i <= 0)
{
throw new ArgumentException($"Invalid environment variable specification (expected 'name=value'): '{arg}'");
}
return (arg.Substring(0, i), arg.Substring(i + 1));
}
public List<string> GetXunitArgsFromTestConfig()
{
List<TestSpecifier> testsToSkip = new();
List<TestList> testLists = new();
List<string> ret = new();
foreach (var testConfigFile in TestConfigFiles)
{
var testConfig = XDocument.Load(testConfigFile);
if (testConfig.Root is not null)
{
foreach (var item in testConfig.Root.Elements())
{
if (item.Name.LocalName.Equals("TestList", StringComparison.OrdinalIgnoreCase))
{
testLists.Add(TestList.Parse(item));
}
else if (item.Name.LocalName.Equals("SkippedTests", StringComparison.OrdinalIgnoreCase))
{
var skippedGroup = TestList.Parse(item);
testsToSkip.AddRange(skippedGroup.TestSpecifiers);
}
else
{
if (bool.TryParse(item.Attribute("Skip")?.Value ?? string.Empty, out bool shouldSkip) &&
shouldSkip)
{
testsToSkip.Add(TestSpecifier.Parse(item));
}
}
}
}
}
foreach (var testList in testLists.Where(g => TestListsToRun.Contains(g.Name ?? string.Empty)))
{
foreach (var testSpec in testList.TestSpecifiers)
{
if (testSpec.Type == TestSpecifier.TestSpecifierType.Method)
{
ret.Add("-method");
}
else if (testSpec.Type == TestSpecifier.TestSpecifierType.Class)
{
ret.Add("-class");
}
else if (testSpec.Type == TestSpecifier.TestSpecifierType.Namespace)
{
ret.Add("-namespace");
}
else
{
throw new ArgumentException("Unrecognized test specifier type: " + testSpec.Type);
}
ret.Add(testSpec.Specifier ?? string.Empty);
}
}
foreach (var testSpec in testsToSkip)
{
if (testSpec.Type == TestSpecifier.TestSpecifierType.Method)
{
ret.Add("-nomethod");
}
else if (testSpec.Type == TestSpecifier.TestSpecifierType.Class)
{
ret.Add("-noclass");
}
else if (testSpec.Type == TestSpecifier.TestSpecifierType.Namespace)
{
ret.Add("-nonamespace");
}
else
{
throw new ArgumentException("Unrecognized test specifier type: " + testSpec.Type);
}
ret.Add(testSpec.Specifier ?? string.Empty);
}
return ret;
}
private class TestList
{
public string? Name { get; set; }
public List<TestSpecifier> TestSpecifiers { get; set; } = new List<TestSpecifier>();
public static TestList Parse(XElement element)
{
TestList group = new()
{
Name = element.Attribute("Name")?.Value
};
foreach (var item in element.Elements())
{
group.TestSpecifiers.Add(TestSpecifier.Parse(item));
}
return group;
}
}
private class TestSpecifier
{
public enum TestSpecifierType
{
Method,
Class,
Namespace
}
public TestSpecifierType Type { get; set; }
public string? Specifier { get; set; }
public static TestSpecifier Parse(XElement element)
{
TestSpecifier spec = new();
switch (element.Name.LocalName.ToLowerInvariant())
{
case "method":
spec.Type = TestSpecifierType.Method;
break;
case "class":
spec.Type = TestSpecifierType.Class;
break;
case "namespace":
spec.Type = TestSpecifierType.Namespace;
break;
default:
throw new XmlException("Unrecognized node: " + element.Name);
}
spec.Specifier = element.Attribute("Name")?.Value;
return spec;
}
}
public static TestCommandLine HandleCommandLine(string[] args)
{
TestCommandLine commandLine = Parse(args);
if (!commandLine.ShouldShowHelp)
{
TestContext.Initialize(commandLine);
}
return commandLine;
}
public static void ShowHelp()
{
Console.WriteLine(
@"
.NET Core SDK test runner
Options to control toolset to test:
-useFullMSBuild : Use full framework (instead of .NET Core) version of MSBuild found in PATH
-fullMSBuildPath <path> : Use full framework version of MSBuild in specified path
-dotnetPath <path> : Use specified path for dotnet host
-sdkRepo <path> : Use specified SDK repo for Microsoft.NET.SDK tasks / targets
-sdkConfig <config> : Use specified configuration for SDK repo
-noRepoInference : Don't automatically find SDK repo to use based on path to test binaries
-sdkVersion : Use specified SDK version
Other options:
-testConfigFile : XML file with tests to skip, or test lists that can be run (can specify multiple)
-testList : List of tests (from config file) which should be run (can specify multiple)
-testExecutionDirectory : Folder for tests to create and build projects
-msbuildAdditionalSdkResolverFolder
: Folder for tests to override 'MsbuildAdditionalSdkResolverFolder' environment variable in tests
in order to test build built sdk resolvers.
-showSdkInfo : Shows SDK info (dotnet --info) for SDK which will be used
-help : Show help");
}
}
}
|