private static async System.Threading.Tasks.Task RunCoverToolAsync(CoverageProject project) { if (project.IsDotNetSdkStyle()) { await CoverletUtil.RunCoverletAsync(project, true); } else { await OpenCoverUtil.RunOpenCoverAsync(project, true); } }
private static List <ReferencedProject> GetReferencedProjects(CoverageProject project) { /* * <ItemGroup> * <ProjectReference Include="..\BranchCoverage\Branch_Coverage.csproj" /> * <ProjectReference Include="..\FxClassLibrary1\FxClassLibrary1.csproj"></ProjectReference> * </ItemGroup> */ var referencedProjects = new List <ReferencedProject>(); var xprojectReferences = project.ProjectFileXElement.XPathSelectElements($"/ItemGroup/ProjectReference[@Include]"); foreach (var xprojectReference in xprojectReferences) { var referencedProject = new ReferencedProject(); // ProjectFile referencedProject.ProjectFile = xprojectReference.Attribute("Include").Value; if (!Path.IsPathRooted(referencedProject.ProjectFile)) { referencedProject.ProjectFile = Path.GetFullPath(Path.Combine(project.ProjectFolder, referencedProject.ProjectFile)); } // ProjectFileXElement referencedProject.ProjectFileXElement = XElementUtil.Load(referencedProject.ProjectFile, true); // HasExcludeFromCodeCoverageAssemblyAttribute referencedProject.HasExcludeFromCodeCoverageAssemblyAttribute = HasExcludeFromCodeCoverageAssemblyAttribute(referencedProject.ProjectFileXElement); // AssemblyName referencedProject.AssemblyName = GetAssemblyName(referencedProject.ProjectFileXElement, Path.GetFileNameWithoutExtension(referencedProject.ProjectFile)); // add referencedProjects.Add(referencedProject); } return(referencedProjects); }
private static AppOptions GetSettings(CoverageProject project) { // get global settings var settings = AppOptions.Get(); /* * ======================================== * Process PropertyGroup settings * ======================================== * <PropertyGroup Label="FineCodeCoverage"> * ... * </PropertyGroup> */ var settingsPropertyGroup = project.ProjectFileXElement.XPathSelectElement($"/PropertyGroup[@Label='{Vsix.Code}']"); if (settingsPropertyGroup != null) { foreach (var property in settings.GetType().GetProperties()) { try { var xproperty = settingsPropertyGroup.Descendants().FirstOrDefault(x => x.Name.LocalName.Equals(property.Name, StringComparison.OrdinalIgnoreCase)); if (xproperty == null) { continue; } var strValue = xproperty.Value; if (string.IsNullOrWhiteSpace(strValue)) { continue; } var strValueArr = strValue.Split('\n', '\r').Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray(); if (!strValue.Any()) { continue; } if (TypeMatch(property.PropertyType, typeof(string))) { property.SetValue(settings, strValueArr.FirstOrDefault()); } else if (TypeMatch(property.PropertyType, typeof(string[]))) { property.SetValue(settings, strValueArr); } else if (TypeMatch(property.PropertyType, typeof(bool), typeof(bool?))) { if (bool.TryParse(strValueArr.FirstOrDefault(), out bool value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(bool[]), typeof(bool?[]))) { var arr = strValueArr.Where(x => bool.TryParse(x, out var _)).Select(x => bool.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(int), typeof(int?))) { if (int.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(int[]), typeof(int?[]))) { var arr = strValueArr.Where(x => int.TryParse(x, out var _)).Select(x => int.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(short), typeof(short?))) { if (short.TryParse(strValueArr.FirstOrDefault(), out var vaue)) { property.SetValue(settings, vaue); } } else if (TypeMatch(property.PropertyType, typeof(short[]), typeof(short?[]))) { var arr = strValueArr.Where(x => short.TryParse(x, out var _)).Select(x => short.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(long), typeof(long?))) { if (long.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(long[]), typeof(long?[]))) { var arr = strValueArr.Where(x => long.TryParse(x, out var _)).Select(x => long.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(decimal), typeof(decimal?))) { if (decimal.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(decimal[]), typeof(decimal?[]))) { var arr = strValueArr.Where(x => decimal.TryParse(x, out var _)).Select(x => decimal.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(double), typeof(double?))) { if (double.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(double[]), typeof(double?[]))) { var arr = strValueArr.Where(x => double.TryParse(x, out var _)).Select(x => double.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(float), typeof(float?))) { if (float.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(float[]), typeof(float?[]))) { var arr = strValueArr.Where(x => float.TryParse(x, out var _)).Select(x => float.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(char), typeof(char?))) { if (char.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(char[]), typeof(char?[]))) { var arr = strValueArr.Where(x => char.TryParse(x, out var _)).Select(x => char.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else { throw new Exception($"Cannot handle '{property.PropertyType.Name}' yet"); } } catch (Exception exception) { Logger.Log($"Failed to override '{property.Name}' setting", exception); } } } // return return(settings); }
private static bool IsDotNetSdkStyle(CoverageProject project) { return(project.ProjectFileXElement .DescendantsAndSelf() .Where(x => { //https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?view=vs-2019 /* * <Project Sdk="My.Custom.Sdk"> * ... * </Project> * <Project Sdk="My.Custom.Sdk/1.2.3"> * ... * </Project> */ if ( x?.Name?.LocalName?.Equals("Project", StringComparison.OrdinalIgnoreCase) == true && x?.Parent == null ) { var sdkAttr = x?.Attributes()?.FirstOrDefault(attr => attr?.Name?.LocalName?.Equals("Sdk", StringComparison.OrdinalIgnoreCase) == true); if (sdkAttr?.Value?.Trim()?.StartsWith("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase) == true) { return true; } } /* * <Project> * <Sdk Name="My.Custom.Sdk" Version="1.2.3" /> * ... * </Project> */ if ( x?.Name?.LocalName?.Equals("Sdk", StringComparison.OrdinalIgnoreCase) == true && x?.Parent?.Name?.LocalName?.Equals("Project", StringComparison.OrdinalIgnoreCase) == true && x?.Parent?.Parent == null ) { var nameAttr = x?.Attributes()?.FirstOrDefault(attr => attr?.Name?.LocalName?.Equals("Name", StringComparison.OrdinalIgnoreCase) == true); if (nameAttr?.Value?.Trim()?.StartsWith("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase) == true) { return true; } } /* * <Project> * <PropertyGroup> * <MyProperty>Value</MyProperty> * </PropertyGroup> * <Import Project="Sdk.props" Sdk="My.Custom.Sdk" /> * ... * <Import Project="Sdk.targets" Sdk="My.Custom.Sdk" /> * </Project> */ if ( x?.Name?.LocalName?.Equals("Import", StringComparison.OrdinalIgnoreCase) == true && x?.Parent?.Name?.LocalName?.Equals("Project", StringComparison.OrdinalIgnoreCase) == true && x?.Parent?.Parent == null ) { var sdkAttr = x?.Attributes()?.FirstOrDefault(attr => attr?.Name?.LocalName?.Equals("Sdk", StringComparison.OrdinalIgnoreCase) == true); if (sdkAttr?.Value?.Trim()?.StartsWith("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase) == true) { return true; } } return false; }) .Any()); }
public static bool RunOpenCover(CoverageProject project, bool throwError = false) { var title = $"OpenCover Run ({project.ProjectName})"; var opencoverSettings = new List <string>(); opencoverSettings.Add($@" -mergebyhash "); opencoverSettings.Add($@" -hideskipped:all "); { // -register: var registerValue = "path32"; if (project.Is64Bit) { registerValue = "path64"; } opencoverSettings.Add($@" -register:{registerValue} "); } { // -target: opencoverSettings.Add($@" ""-target:{MsTestPlatformUtil.MsTestPlatformExePath}"" "); } { // -filter: var filters = new List <string>(); var defaultFilter = "+[*]*"; foreach (var value in (project.Settings.Include ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { filters.Add($@"+{value.Replace("\"", "\\\"").Trim(' ', '\'')}"); } if (!filters.Any()) { filters.Add(defaultFilter); } foreach (var value in (project.Settings.Exclude ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { filters.Add($@"-{value.Replace("\"", "\\\"").Trim(' ', '\'')}"); } foreach (var referenceProjectWithExcludeAttribute in project.ReferencedProjects.Where(x => x.HasExcludeFromCodeCoverageAssemblyAttribute)) { filters.Add($@"-[{referenceProjectWithExcludeAttribute.AssemblyName}]*"); } if (filters.Any(x => !x.Equals(defaultFilter))) { opencoverSettings.Add($@" ""-filter:{string.Join(" ", filters.Distinct())}"" "); } } { // -excludebyfile: var excludes = new List <string>(); foreach (var value in (project.Settings.ExcludeByFile ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { excludes.Add(value.Replace("\"", "\\\"").Trim(' ', '\'')); } if (excludes.Any()) { opencoverSettings.Add($@" ""-excludebyfile:{string.Join(";", excludes)}"" "); } } { // -excludebyattribute: var excludes = new List <string>() { // coverlet knows these implicitly "ExcludeFromCoverage", "ExcludeFromCodeCoverage" }; foreach (var value in (project.Settings.ExcludeByAttribute ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { excludes.Add(value.Replace("\"", "\\\"").Trim(' ', '\'')); } foreach (var exclude in excludes.ToArray()) { var excludeAlternateName = default(string); if (exclude.EndsWith("Attribute", StringComparison.OrdinalIgnoreCase)) { // remove 'Attribute' suffix excludeAlternateName = exclude.Substring(0, exclude.IndexOf("Attribute", StringComparison.OrdinalIgnoreCase)); } else { // add 'Attribute' suffix excludeAlternateName = $"{exclude}Attribute"; } excludes.Add(excludeAlternateName); } excludes = excludes.Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(x => x).ToList(); if (excludes.Any()) { opencoverSettings.Add($@" ""-excludebyattribute:(*.{string.Join(")|(*.", excludes)})"" "); } } if (!project.Settings.IncludeTestAssembly) { // deleting the pdb of the test assembly seems to work; this is a VERY VERY shameful hack :( var testDllPdbFile = Path.Combine(project.ProjectOutputFolder, Path.GetFileNameWithoutExtension(project.TestDllFile)) + ".pdb"; File.Delete(testDllPdbFile); // filtering out the test-assembly blows up the entire process and nothing gets instrumented or analysed //var nameOnlyOfDll = Path.GetFileNameWithoutExtension(project.TestDllFileInWorkFolder); //filters.Add($@"-[{nameOnlyOfDll}]*"); } var runSettings = !string.IsNullOrWhiteSpace(project.RunSettingsFile) ? $@"/Settings:\""{project.RunSettingsFile}\""" : default; opencoverSettings.Add($@" ""-targetargs:\""{project.TestDllFile}\"" {runSettings}"" "); opencoverSettings.Add($@" ""-output:{ project.CoverageOutputFile }"" "); Logger.Log($"{title} Arguments {Environment.NewLine}{string.Join($"{Environment.NewLine}", opencoverSettings)}"); var result = ProcessUtil .ExecuteAsync(new ExecuteRequest { FilePath = OpenCoverExePath, Arguments = string.Join(" ", opencoverSettings), WorkingDirectory = project.ProjectOutputFolder }) .GetAwaiter() .GetResult(); if (result.ExitCode != 0) { if (throwError) { throw new Exception(result.Output); } Logger.Log($"{title} Error", result.Output); return(false); } Logger.Log(title, result.Output); return(true); }
public static async Task <bool> RunCoverletAsync(CoverageProject project, bool throwError = false) { var title = $"Coverlet Run ({project.ProjectName})"; var coverletSettings = new List <string>(); coverletSettings.Add($@"""{project.TestDllFile}"""); coverletSettings.Add($@"--format ""cobertura"""); foreach (var value in (project.Settings.Exclude ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--exclude ""{value.Replace("\"", "\\\"").Trim(' ', '\'')}"""); } foreach (var referenceProjectWithExcludeAttribute in project.ReferencedProjects.Where(x => x.ExcludeFromCodeCoverage)) { coverletSettings.Add($@"--exclude ""[{referenceProjectWithExcludeAttribute.AssemblyName}]*"""); } foreach (var value in (project.Settings.Include ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--include ""{value.Replace("\"", "\\\"").Trim(' ', '\'')}"""); } foreach (var value in (project.Settings.ExcludeByFile ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--exclude-by-file ""{value.Replace("\"", "\\\"").Trim(' ', '\'')}"""); } foreach (var value in (project.Settings.ExcludeByAttribute ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--exclude-by-attribute ""{value.Replace("\"", "\\\"").Trim(' ', '\'', '[', ']')}"""); } if (project.Settings.IncludeTestAssembly) { coverletSettings.Add("--include-test-assembly"); } coverletSettings.Add($@"--target ""dotnet"""); coverletSettings.Add($@"--threshold-type line"); coverletSettings.Add($@"--threshold-stat total"); coverletSettings.Add($@"--threshold 0"); coverletSettings.Add($@"--output ""{ project.CoverageOutputFile }"""); var runSettings = !string.IsNullOrWhiteSpace(project.RunSettingsFile) ? $@"--settings """"{project.RunSettingsFile}""""" : default; coverletSettings.Add($@"--targetargs ""test """"{project.TestDllFile}"""" --nologo --blame {runSettings} --results-directory """"{project.CoverageOutputFolder}"""" --diag """"{project.CoverageOutputFolder}/diagnostics.log"""" """); Logger.Log($"{title} Arguments {Environment.NewLine}{string.Join($"{Environment.NewLine}", coverletSettings)}"); var result = await ProcessUtil .ExecuteAsync(new ExecuteRequest { FilePath = CoverletExePath, Arguments = string.Join(" ", coverletSettings), WorkingDirectory = project.ProjectOutputFolder }); if (result != null) { /* * 0 - Success. * 1 - If any test fails. * 2 - Coverage percentage is below threshold. * 3 - Test fails and also coverage percentage is below threshold. */ if (result.ExitCode > 3) { if (throwError) { throw new Exception(result.Output); } Logger.Log($"{title} Error", result.Output); return(false); } Logger.Log(title, result.Output); return(true); } return(false); }
public static bool RunCoverlet(CoverageProject project, bool throwError = false) { var title = $"Coverlet Run ({project.ProjectName})"; if (File.Exists(project.CoverToolOutputFile)) { File.Delete(project.CoverToolOutputFile); } if (Directory.Exists(project.WorkOutputFolder)) { Directory.Delete(project.WorkOutputFolder, true); } Directory.CreateDirectory(project.WorkOutputFolder); var coverletSettings = new List <string>(); coverletSettings.Add($@"""{project.TestDllFileInWorkFolder}"""); coverletSettings.Add($@"--format ""cobertura"""); foreach (var value in (project.Settings.Exclude ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--exclude ""{value.Replace("\"", "\\\"").Trim(' ', '\'')}"""); } foreach (var value in (project.Settings.Include ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--include ""{value.Replace("\"", "\\\"").Trim(' ', '\'')}"""); } foreach (var value in (project.Settings.ExcludeByFile ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--exclude-by-file ""{value.Replace("\"", "\\\"").Trim(' ', '\'')}"""); } foreach (var value in (project.Settings.ExcludeByAttribute ?? new string[0]).Where(x => !string.IsNullOrWhiteSpace(x))) { coverletSettings.Add($@"--exclude-by-attribute ""{value.Replace("\"", "\\\"").Trim(' ', '\'', '[', ']')}"""); } if (project.Settings.IncludeTestAssembly) { coverletSettings.Add("--include-test-assembly"); } coverletSettings.Add($@"--target ""dotnet"""); coverletSettings.Add($@"--threshold-type line"); coverletSettings.Add($@"--threshold-stat total"); coverletSettings.Add($@"--threshold 0"); coverletSettings.Add($@"--output ""{ project.CoverToolOutputFile }"""); coverletSettings.Add($@"--targetargs ""test """"{project.TestDllFileInWorkFolder}"""" --nologo --blame --results-directory """"{project.WorkOutputFolder}"""" --diag """"{project.WorkOutputFolder}/diagnostics.log"""" """); Logger.Log($"{title} Arguments {Environment.NewLine}{string.Join($"{Environment.NewLine}", coverletSettings)}"); var result = ProcessUtil .ExecuteAsync(new ExecuteRequest { FilePath = CoverletExePath, Arguments = string.Join(" ", coverletSettings), WorkingDirectory = project.WorkFolder }) .GetAwaiter() .GetResult(); /* * 0 - Success. * 1 - If any test fails. * 2 - Coverage percentage is below threshold. * 3 - Test fails and also coverage percentage is below threshold. */ if (result.ExitCode > 3) { if (throwError) { throw new Exception(result.Output); } Logger.Log($"{title} Error", result.Output); return(false); } Logger.Log(title, result.Output); return(true); }
private static AppOptions GetSettings(CoverageProject project) { // get global settings var settings = AppOptions.Get(); // override with test project settings XElement xproject; try { xproject = XElement.Parse(File.ReadAllText(project.ProjectFile)); } catch (Exception ex) { Logger.Log("Failed to parse project file", ex); return(settings); } var xsettings = xproject.Descendants("PropertyGroup").FirstOrDefault(x => { var label = x.Attribute("Label")?.Value?.Trim() ?? string.Empty; if (!Vsix.Code.Equals(label, StringComparison.OrdinalIgnoreCase)) { return(false); } return(true); }); if (xsettings == null) { return(settings); } foreach (var property in settings.GetType().GetProperties()) { try { var xproperty = xsettings.Descendants().FirstOrDefault(x => x.Name.LocalName.Equals(property.Name, StringComparison.OrdinalIgnoreCase)); if (xproperty == null) { continue; } var strValue = xproperty.Value; if (string.IsNullOrWhiteSpace(strValue)) { continue; } var strValueArr = strValue.Split('\n', '\r').Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray(); if (!strValue.Any()) { continue; } if (TypeMatch(property.PropertyType, typeof(string))) { property.SetValue(settings, strValueArr.FirstOrDefault()); } else if (TypeMatch(property.PropertyType, typeof(string[]))) { property.SetValue(settings, strValueArr); } else if (TypeMatch(property.PropertyType, typeof(bool), typeof(bool?))) { if (bool.TryParse(strValueArr.FirstOrDefault(), out bool value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(bool[]), typeof(bool?[]))) { var arr = strValueArr.Where(x => bool.TryParse(x, out var _)).Select(x => bool.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(int), typeof(int?))) { if (int.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(int[]), typeof(int?[]))) { var arr = strValueArr.Where(x => int.TryParse(x, out var _)).Select(x => int.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(short), typeof(short?))) { if (short.TryParse(strValueArr.FirstOrDefault(), out var vaue)) { property.SetValue(settings, vaue); } } else if (TypeMatch(property.PropertyType, typeof(short[]), typeof(short?[]))) { var arr = strValueArr.Where(x => short.TryParse(x, out var _)).Select(x => short.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(long), typeof(long?))) { if (long.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(long[]), typeof(long?[]))) { var arr = strValueArr.Where(x => long.TryParse(x, out var _)).Select(x => long.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(decimal), typeof(decimal?))) { if (decimal.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(decimal[]), typeof(decimal?[]))) { var arr = strValueArr.Where(x => decimal.TryParse(x, out var _)).Select(x => decimal.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(double), typeof(double?))) { if (double.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(double[]), typeof(double?[]))) { var arr = strValueArr.Where(x => double.TryParse(x, out var _)).Select(x => double.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(float), typeof(float?))) { if (float.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(float[]), typeof(float?[]))) { var arr = strValueArr.Where(x => float.TryParse(x, out var _)).Select(x => float.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else if (TypeMatch(property.PropertyType, typeof(char), typeof(char?))) { if (char.TryParse(strValueArr.FirstOrDefault(), out var value)) { property.SetValue(settings, value); } } else if (TypeMatch(property.PropertyType, typeof(char[]), typeof(char?[]))) { var arr = strValueArr.Where(x => char.TryParse(x, out var _)).Select(x => char.Parse(x)); if (arr.Any()) { property.SetValue(settings, arr); } } else { throw new Exception($"Cannot handle '{property.PropertyType.Name}' yet"); } } catch (Exception exception) { Logger.Log($"Failed to override '{property.Name}' setting", exception); } } // return return(settings); }