[Harness] Make Harness properties immutable (#8093)

Co-authored-by: Premek Vysoky <prvysoky@microsoft.com>
This commit is contained in:
Přemek Vysoký 2020-03-13 15:41:02 +01:00 коммит произвёл GitHub
Родитель f21f563eb3
Коммит ef9f7e4e9b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 152 добавлений и 87 удалений

Просмотреть файл

@ -31,16 +31,40 @@ namespace Xharness
Version XcodeVersion { get; }
Task<ProcessExecutionResult> ExecuteXcodeCommandAsync (string executable, IList<string> args, ILog log, TimeSpan timeout);
}
public class HarnessConfiguration {
public bool AutoConf { get; set; }
public string Configuration { get; set; } = "Debug";
public bool DryRun { get; set; }
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string> ();
public bool? IncludeSystemPermissionTests { get; set; }
public List<iOSTestProject> IOSTestProjects { get; set; } = new List<iOSTestProject> ();
public string JenkinsConfiguration { get; set; }
public HashSet<string> Labels { get; set; } = new HashSet<string> ();
public string LogDirectory { get; set; } = Environment.CurrentDirectory;
public bool Mac { get; set; }
public string MarkdownSummaryPath { get; set; }
public string PeriodicCommand { get; set; }
public string PeriodicCommandArguments { get; set; }
public TimeSpan PeriodicCommandInterval { get; set; }
public string RootDirectory { get; set; }
public string SdkRoot { get; set; }
public AppRunnerTarget Target { get; set; }
public double TimeoutInMinutes { get; set; } = 15;
public bool UseSystemXamarinIOSMac { get; set; }
public int Verbosity { get; set; }
public string WatchOSAppTemplate { get; set; }
public string WatchOSContainerTemplate { get; set; }
public XmlResultJargon XmlJargon { get; set; } = XmlResultJargon.NUnitV3;
}
public class Harness : IHarness
{
public HarnessAction Action { get; set; }
public int Verbosity { get; set; }
public HarnessAction Action { get; }
public int Verbosity { get; }
public ILog HarnessLog { get; set; }
public bool UseSystem { get; set; } // if the system XI/XM should be used, or the locally build XI/XM.
public HashSet<string> Labels { get; } = new HashSet<string> ();
public XmlResultJargon XmlJargon { get; set; } = XmlResultJargon.NUnitV3;
public IProcessManager ProcessManager { get; set; } = new ProcessManager ();
public HashSet<string> Labels { get; }
public XmlResultJargon XmlJargon { get; }
public IProcessManager ProcessManager { get; }
public string XIBuildPath {
get { return Path.GetFullPath (Path.Combine (RootDirectory, "..", "tools", "xibuild", "xibuild")); }
@ -80,53 +104,87 @@ namespace Xharness
}
}
public List<iOSTestProject> IOSTestProjects { get; set; } = new List<iOSTestProject> ();
public List<MacTestProject> MacTestProjects { get; set; } = new List<MacTestProject> ();
public List<iOSTestProject> IOSTestProjects { get; }
public List<MacTestProject> MacTestProjects { get; } = new List<MacTestProject> ();
// Configure
public bool AutoConf { get; set; }
public bool Mac { get; set; }
public string WatchOSContainerTemplate { get; set; }
public string WatchOSAppTemplate { get; set; }
public string WatchOSExtensionTemplate { get; set; }
public string TodayContainerTemplate { get; set; }
public string TodayExtensionTemplate { get; set; }
public string BCLTodayExtensionTemplate { get; set; }
public string MONO_PATH { get; set; } // Use same name as in Makefiles, so that a grep finds it.
public string TVOS_MONO_PATH { get; set; } // Use same name as in Makefiles, so that a grep finds it.
public bool INCLUDE_IOS { get; set; }
public bool INCLUDE_TVOS { get; set; }
public bool INCLUDE_WATCH { get; set; }
public bool INCLUDE_MAC { get; set; }
public string JENKINS_RESULTS_DIRECTORY { get; set; } // Use same name as in Makefiles, so that a grep finds it.
public string MAC_DESTDIR { get; set; }
public string IOS_DESTDIR { get; set; }
public string MONO_IOS_SDK_DESTDIR { get; set; }
public string MONO_MAC_SDK_DESTDIR { get; set; }
public bool IncludeMac32 { get; set; }
public bool ENABLE_XAMARIN { get; set; }
public string DOTNET { get; set; }
readonly bool useSystemXamarinIOSMac; // if the system XI/XM should be used, or the locally build XI/XM.
readonly bool autoConf;
readonly bool mac;
public string WatchOSContainerTemplate { get; private set; }
public string WatchOSAppTemplate { get; private set; }
public string WatchOSExtensionTemplate { get; private set; }
public string TodayContainerTemplate { get; private set; }
public string TodayExtensionTemplate { get; private set; }
public string BCLTodayExtensionTemplate { get; private set; }
public string MONO_PATH { get; private set; } // Use same name as in Makefiles, so that a grep finds it.
public string TVOS_MONO_PATH { get; private set; } // Use same name as in Makefiles, so that a grep finds it.
public bool INCLUDE_IOS { get; private set; }
public bool INCLUDE_TVOS { get; private set; }
public bool INCLUDE_WATCH { get; private set; }
public bool INCLUDE_MAC { get; private set; }
public string JENKINS_RESULTS_DIRECTORY { get; private set; } // Use same name as in Makefiles, so that a grep finds it.
public string MAC_DESTDIR { get; private set; }
public string IOS_DESTDIR { get; private set; }
public string MONO_IOS_SDK_DESTDIR { get; private set; }
public string MONO_MAC_SDK_DESTDIR { get; private set; }
public bool ENABLE_XAMARIN { get; private set; }
public string DOTNET { get; private set; }
// Run
public AppRunnerTarget Target { get; set; }
public string SdkRoot { get; set; }
public string Configuration { get; set; } = "Debug";
public string LogFile { get; set; }
public string LogDirectory { get; set; } = Environment.CurrentDirectory;
public double Timeout { get; set; } = 15; // in minutes
public double LaunchTimeout { get; set; } // in minutes
public bool DryRun { get; set; } // Most things don't support this. If you need it somewhere, implement it!
public string JenkinsConfiguration { get; set; }
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string> ();
public string MarkdownSummaryPath { get; set; }
public string PeriodicCommand { get; set; }
public string PeriodicCommandArguments { get; set; }
public TimeSpan PeriodicCommandInterval { get; set; }
public AppRunnerTarget Target { get; }
public string SdkRoot { get; private set; }
public string Configuration { get; }
public string LogDirectory { get; }
public double Timeout { get; } = 15; // in minutes
public double LaunchTimeout { get; } // in minutes
public bool DryRun { get; } // Most things don't support this. If you need it somewhere, implement it!
public string JenkinsConfiguration { get; }
public Dictionary<string, string> EnvironmentVariables { get; }
public string MarkdownSummaryPath { get; }
public string PeriodicCommand { get; }
public string PeriodicCommandArguments { get; }
public TimeSpan PeriodicCommandInterval { get; }
// whether tests that require access to system resources (system contacts, photo library, etc) should be executed or not
public bool? IncludeSystemPermissionTests { get; set; }
public Harness ()
public Harness (IProcessManager processManager, HarnessAction action, HarnessConfiguration configuration)
{
ProcessManager = processManager ?? throw new ArgumentNullException (nameof (processManager));
Action = action;
if (configuration is null)
throw new ArgumentNullException (nameof (configuration));
autoConf = configuration.AutoConf;
Configuration = configuration.Configuration ?? throw new ArgumentNullException (nameof (configuration));
DryRun = configuration.DryRun;
IncludeSystemPermissionTests = configuration.IncludeSystemPermissionTests;
IOSTestProjects = configuration.IOSTestProjects;
JenkinsConfiguration = configuration.JenkinsConfiguration;
LogDirectory = configuration.LogDirectory ?? throw new ArgumentNullException (nameof (configuration.LogDirectory));
mac = configuration.Mac;
MarkdownSummaryPath = configuration.MarkdownSummaryPath;
PeriodicCommand = configuration.PeriodicCommand;
PeriodicCommandArguments = configuration.PeriodicCommandArguments;
PeriodicCommandInterval = configuration.PeriodicCommandInterval;
RootDirectory = configuration.RootDirectory;
SdkRoot = configuration.SdkRoot;
Target = configuration.Target;
Timeout = configuration.TimeoutInMinutes;
useSystemXamarinIOSMac = configuration.UseSystemXamarinIOSMac;
Verbosity = configuration.Verbosity;
WatchOSAppTemplate = configuration.WatchOSAppTemplate;
WatchOSContainerTemplate = configuration.WatchOSContainerTemplate;
XmlJargon = configuration.XmlJargon;
if (configuration.Labels != null)
Labels = new HashSet<string> (configuration.Labels);
if (configuration.EnvironmentVariables != null)
EnvironmentVariables = new Dictionary<string, string> (configuration.EnvironmentVariables);
LaunchTimeout = InCI ? 3 : 120;
}
@ -452,7 +510,7 @@ namespace Xharness
void ParseConfigFiles ()
{
ParseConfigFiles (FindConfigFiles (UseSystem ? "test-system.config" : "test.config"));
ParseConfigFiles (FindConfigFiles (useSystemXamarinIOSMac ? "test-system.config" : "test.config"));
ParseConfigFiles (FindConfigFiles ("Make.config.local"));
ParseConfigFiles (FindConfigFiles ("Make.config"));
}
@ -480,7 +538,7 @@ namespace Xharness
public int Configure ()
{
return Mac ? AutoConfigureMac (true) : ConfigureIOS ();
return mac ? AutoConfigureMac (true) : ConfigureIOS ();
}
int ConfigureIOS ()
@ -491,7 +549,7 @@ namespace Xharness
var watchos_targets = new List<WatchOSTarget> ();
var today_targets = new List<TodayExtensionTarget> ();
if (AutoConf)
if (autoConf)
AutoConfigureIOS ();
foreach (var monoNativeInfo in IOSTestProjects.Where (x => x.MonoNativeInfo != null).Select (x => x.MonoNativeInfo))
@ -691,7 +749,7 @@ namespace Xharness
public int Jenkins ()
{
if (AutoConf) {
if (autoConf) {
AutoConfigureIOS ();
AutoConfigureMac (false);
}

Просмотреть файл

@ -1,82 +1,85 @@
using System;
using System.Collections.Generic;
using Mono.Options;
using Xharness.Execution;
using Xharness.Utilities;
namespace Xharness {
class MainClass {
public static int Main (string [] args)
{
var harness = new Harness ();
Action showHelp = null;
var action = HarnessAction.None;
var configuration = new HarnessConfiguration();
var os = new OptionSet () {
{ "h|?|help", "Displays the help", (v) => showHelp () },
{ "v|verbose", "Show verbose output", (v) => harness.Verbosity++ },
{ "use-system:", "Use the system version of Xamarin.iOS/Xamarin.Mac or the locally build version. Default: the locally build version.", (v) => harness.UseSystem = v == "1" || v == "true" || string.IsNullOrEmpty (v) },
{ "v|verbose", "Show verbose output", (v) => configuration.Verbosity++ },
{ "use-system:", "Use the system version of Xamarin.iOS/Xamarin.Mac or the locally build version. Default: the locally build version.", (v) => configuration.UseSystemXamarinIOSMac = v == "1" || v == "true" || string.IsNullOrEmpty (v) },
// Configure
{ "mac", "Configure for Xamarin.Mac instead of iOS.", (v) => harness.Mac = true },
{ "configure", "Creates project files and makefiles.", (v) => harness.Action = HarnessAction.Configure },
{ "autoconf", "Automatically decide what to configure.", (v) => harness.AutoConf = true },
{ "rootdir=", "The root directory for the tests.", (v) => Harness.RootDirectory = v },
{ "project=", "Add a project file to process. This can be specified multiple times.", (v) => harness.IOSTestProjects.Add (new iOSTestProject (v)) },
{ "watchos-container-template=", "The directory to use as a template for a watchos container app.", (v) => harness.WatchOSContainerTemplate = v },
{ "watchos-app-template=", "The directory to use as a template for a watchos app.", (v) => harness.WatchOSAppTemplate = v },
{ "mac", "Configure for Xamarin.Mac instead of iOS.", (v) => configuration.Mac = true },
{ "configure", "Creates project files and makefiles.", (v) => action = HarnessAction.Configure },
{ "autoconf", "Automatically decide what to configure.", (v) => configuration.AutoConf = true },
{ "rootdir=", "The root directory for the tests.", (v) => configuration.RootDirectory = v },
{ "project=", "Add a project file to process. This can be specified multiple times.", (v) => configuration.IOSTestProjects.Add (new iOSTestProject (v)) },
{ "watchos-container-template=", "The directory to use as a template for a watchos container app.", (v) => configuration.WatchOSContainerTemplate = v },
{ "watchos-app-template=", "The directory to use as a template for a watchos app.", (v) => configuration.WatchOSAppTemplate = v },
// Run
{ "run=", "Executes a project.", (v) =>
{
harness.Action = HarnessAction.Run;
harness.IOSTestProjects.Add (new iOSTestProject (v));
action = HarnessAction.Run;
configuration.IOSTestProjects.Add (new iOSTestProject (v));
}
},
{ "install=", "Installs a project.", (v) =>
{
harness.Action = HarnessAction.Install;
harness.IOSTestProjects.Add (new iOSTestProject (v));
action = HarnessAction.Install;
configuration.IOSTestProjects.Add (new iOSTestProject (v));
}
},
{ "uninstall=", "Uninstalls a project.", (v) =>
{
harness.Action = HarnessAction.Uninstall;
harness.IOSTestProjects.Add (new iOSTestProject (v));
action = HarnessAction.Uninstall;
configuration.IOSTestProjects.Add (new iOSTestProject (v));
}
},
{ "sdkroot=", "Where Xcode is", (v) => harness.SdkRoot = v },
{ "sdkroot=", "Where Xcode is", (v) => configuration.SdkRoot = v },
{ "sdkroot94=", "Where Xcode 9.4 is", (v) => Console.WriteLine ("--sdkroot94 is deprecated"), true },
{ "target=", "Where to run the project ([ios|watchos|tvos]-[device|simulator|simulator-32|simulator-64]).", (v) => harness.Target = v.ParseAsAppRunnerTarget () },
{ "configuration=", "Which configuration to run (defaults to Debug).", (v) => harness.Configuration = v },
{ "logdirectory=", "Where to store logs.", (v) => harness.LogDirectory = v },
{ "logfile=", "Where to store the log.", (v) => harness.LogFile = v },
{ "timeout=", "Timeout for a test run (in minutes). Default is 10 minutes.", (v) => harness.Timeout = double.Parse (v) },
{ "target=", "Where to run the project ([ios|watchos|tvos]-[device|simulator|simulator-32|simulator-64]).", (v) => configuration.Target = v.ParseAsAppRunnerTarget () },
{ "configuration=", "Which configuration to run (defaults to Debug).", (v) => configuration.Configuration = v },
{ "logdirectory=", "Where to store logs.", (v) => configuration.LogDirectory = v },
{ "logfile=", "Where to store the log.", (v) => Console.WriteLine("The logfile option is deprecated. Please use logdirectory."), true },
{ "timeout=", $"Timeout for a test run (in minutes). Default is {configuration.TimeoutInMinutes} minutes.", (v) => configuration.TimeoutInMinutes = double.Parse (v) },
{ "jenkins:", "Execute test run for jenkins.", (v) =>
{
harness.JenkinsConfiguration = v;
harness.Action = HarnessAction.Jenkins;
configuration.JenkinsConfiguration = v;
action = HarnessAction.Jenkins;
}
},
{ "dry-run", "Only print what would be done.", (v) => harness.DryRun = true },
{ "dry-run", "Only print what would be done.", (v) => configuration.DryRun = true },
{ "setenv:", "Set the specified environment variable when running apps.", (v) =>
{
var split = v.Split ('=');
harness.EnvironmentVariables [split [0]] = split [1];
configuration.EnvironmentVariables [split [0]] = split [1];
}
},
{ "label=", "Comma-separated list of labels to select which tests to run.", (v) =>
{
harness.Labels.UnionWith (v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries));
configuration.Labels.UnionWith (v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries));
}
},
{ "markdown-summary=", "The path where a summary (in Markdown format) will be written.", (v) => harness.MarkdownSummaryPath = v },
{ "periodic-command=", "A command to execute periodically.", (v) => harness.PeriodicCommand = v },
{ "periodic-command-arguments=", "Arguments to the command to execute periodically.", (v) => harness.PeriodicCommandArguments = v },
{ "periodic-interval=", "An interval (in minutes) between every attempt to execute the periodic command.", (v) => harness.PeriodicCommandInterval = TimeSpan.FromMinutes (double.Parse (v)) },
{ "include-system-permission-tests:", "If tests that require system permissions (which could cause the OS to launch dialogs that hangs the test) should be executed or not. Default is to include such tests.", (v) => harness.IncludeSystemPermissionTests = ParseBool (v, "include-system-permission-tests") },
{ "markdown-summary=", "The path where a summary (in Markdown format) will be written.", (v) => configuration.MarkdownSummaryPath = v },
{ "periodic-command=", "A command to execute periodically.", (v) => configuration.PeriodicCommand = v },
{ "periodic-command-arguments=", "Arguments to the command to execute periodically.", (v) => configuration.PeriodicCommandArguments = v },
{ "periodic-interval=", "An interval (in minutes) between every attempt to execute the periodic command.", (v) => configuration.PeriodicCommandInterval = TimeSpan.FromMinutes (double.Parse (v)) },
{ "include-system-permission-tests:", "If tests that require system permissions (which could cause the OS to launch dialogs that hangs the test) should be executed or not. Default is to include such tests.", (v) => configuration.IncludeSystemPermissionTests = ParseBool (v, "include-system-permission-tests") },
{ "xml-jargon:", "The xml format to be used for test results. Values can be nunitv2, nunitv3, xunit.", (v) =>
{
if (Enum.TryParse<XmlResultJargon> (v, out var jargon))
harness.XmlJargon = jargon;
configuration.XmlJargon = jargon;
else
harness.XmlJargon = XmlResultJargon.Missing;
configuration.XmlJargon = XmlResultJargon.Missing;
}
},
@ -90,16 +93,20 @@ namespace Xharness {
var input = os.Parse (args);
if (input.Count > 0)
throw new Exception (string.Format ("Unknown arguments: {0}", string.Join (", ", input.ToArray ())));
if (harness.Action == HarnessAction.None)
if (action == HarnessAction.None)
showHelp ();
if (harness.XmlJargon == XmlResultJargon.Missing) {
if (configuration.XmlJargon == XmlResultJargon.Missing) {
Console.WriteLine ("Unknown xml-jargon value provided. Values can be nunitv2, nunitv3, xunit");
return 1;
}
// XS sets this, which breaks pretty much everything if it doesn't match what was passed to --sdkroot.
Environment.SetEnvironmentVariable ("XCODE_DEVELOPER_DIR_PATH", null);
var harness = new Harness (new ProcessManager(), action, configuration);
return harness.Execute ();
}