Skip to content

[MTP] Improve performance of validating command line options #5655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,40 +101,66 @@
Dictionary<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> extensionOptionsByProvider,
Dictionary<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> systemOptionsByProvider)
{
IEnumerable<string> allExtensionOptions = extensionOptionsByProvider.Values.SelectMany(x => x).Select(x => x.Name).Distinct();
IEnumerable<string> allSystemOptions = systemOptionsByProvider.Values.SelectMany(x => x).Select(x => x.Name).Distinct();

IEnumerable<string> invalidReservedOptions = allSystemOptions.Intersect(allExtensionOptions);
if (invalidReservedOptions.Any())
// Create a HashSet of all system option names for faster lookup
HashSet<string> systemOptionNames = new();

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 105 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L105

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(105,45): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)
foreach (KeyValuePair<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> provider in systemOptionsByProvider)
{
var stringBuilder = new StringBuilder();
foreach (string reservedOption in invalidReservedOptions)
foreach (CommandLineOption option in provider.Value)
{
IEnumerable<string> faultyProviderNames = extensionOptionsByProvider.Where(tuple => tuple.Value.Any(x => x.Name == reservedOption)).Select(tuple => tuple.Key.DisplayName);
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsReserved, reservedOption, string.Join("', '", faultyProviderNames)));
systemOptionNames.Add(option.Name);
}
}

return ValidationResult.Invalid(stringBuilder.ToTrimmedString());
StringBuilder? stringBuilder = null;
foreach (KeyValuePair<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> provider in extensionOptionsByProvider)
{
foreach (CommandLineOption option in provider.Value)
{
if (systemOptionNames.Contains(option.Name))
{
stringBuilder ??= new StringBuilder();
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsReserved, option.Name, provider.Key.DisplayName));
}
}
}

return ValidationResult.Valid();
return stringBuilder?.Length > 0
? ValidationResult.Invalid(stringBuilder.ToTrimmedString())
: ValidationResult.Valid();
}

private static ValidationResult ValidateOptionsAreNotDuplicated(
Dictionary<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> extensionOptionsByProvider)
{
IEnumerable<string> duplications = extensionOptionsByProvider.Values.SelectMany(x => x)
.Select(x => x.Name)
.GroupBy(x => x)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key);
// Use a dictionary to track option names and their providers
Dictionary<string, List<ICommandLineOptionsProvider>> optionNameToProviders = new();

Check failure on line 136 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L136

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(136,87): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 136 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L136

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(136,87): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 136 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L136

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(136,87): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 136 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L136

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(136,87): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 136 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L136

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(136,87): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)
foreach (KeyValuePair<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> kvp in extensionOptionsByProvider)
{
ICommandLineOptionsProvider provider = kvp.Key;
foreach (CommandLineOption option in kvp.Value)
{
string name = option.Name;
if (!optionNameToProviders.TryGetValue(name, out List<ICommandLineOptionsProvider>? providers))
{
providers = new List<ICommandLineOptionsProvider>();

Check failure on line 145 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L145

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(145,33): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 145 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L145

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(145,33): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 145 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L145

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(145,33): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 145 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L145

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(145,33): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 145 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L145

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(145,33): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)
optionNameToProviders[name] = providers;
}

providers.Add(provider);
}
}

// Check for duplications
StringBuilder? stringBuilder = null;
foreach (string duplicatedOption in duplications)
foreach (KeyValuePair<string, List<ICommandLineOptionsProvider>> kvp in optionNameToProviders)
{
IEnumerable<string> faultyProvidersDisplayNames = extensionOptionsByProvider.Where(tuple => tuple.Value.Any(x => x.Name == duplicatedOption)).Select(tuple => tuple.Key.DisplayName);
stringBuilder ??= new();
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsDeclaredByMultipleProviders, duplicatedOption, string.Join("', '", faultyProvidersDisplayNames)));
if (kvp.Value.Count > 1)
{
string duplicatedOption = kvp.Key;
stringBuilder ??= new();
IEnumerable<string> faultyProvidersDisplayNames = kvp.Value.Select(p => p.DisplayName);
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsDeclaredByMultipleProviders, duplicatedOption, string.Join("', '", faultyProvidersDisplayNames)));
}
}

return stringBuilder?.Length > 0
Expand All @@ -147,10 +173,28 @@
Dictionary<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> extensionOptionsByProvider,
Dictionary<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> systemOptionsByProvider)
{
// Create a HashSet of all valid option names for faster lookup
HashSet<string> validOptionNames = new();

Check failure on line 177 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L177

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(177,44): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 177 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L177

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(177,44): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 177 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build MacOS Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L177

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(177,44): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 177 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Debug)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L177

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(177,44): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 177 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Release)

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L177

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(177,44): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)

Check failure on line 177 in src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs#L177

src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs(177,44): error IDE0028: (NETCORE_ENGINEERING_TELEMETRY=Build) Collection initialization can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028)
foreach (KeyValuePair<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> provider in extensionOptionsByProvider)
{
foreach (CommandLineOption option in provider.Value)
{
validOptionNames.Add(option.Name);
}
}

foreach (KeyValuePair<ICommandLineOptionsProvider, IReadOnlyCollection<CommandLineOption>> provider in systemOptionsByProvider)
{
foreach (CommandLineOption option in provider.Value)
{
validOptionNames.Add(option.Name);
}
}

StringBuilder? stringBuilder = null;
foreach (CommandLineParseOption optionRecord in parseResult.Options)
{
if (!extensionOptionsByProvider.Union(systemOptionsByProvider).Any(tuple => tuple.Value.Any(x => x.Name == optionRecord.Name)))
if (!validOptionNames.Contains(optionRecord.Name))
{
stringBuilder ??= new();
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineUnknownOption, optionRecord.Name));
Expand All @@ -166,7 +210,7 @@
CommandLineParseResult parseResult,
Dictionary<string, (ICommandLineOptionsProvider Provider, CommandLineOption Option)> providerAndOptionByOptionName)
{
StringBuilder stringBuilder = new();
StringBuilder? stringBuilder = null;
foreach (IGrouping<string, CommandLineParseOption> groupedOptions in parseResult.Options.GroupBy(x => x.Name))
{
// getting the arguments count for an option.
Expand All @@ -181,19 +225,22 @@

if (arity > option.Arity.Max && option.Arity.Max == 0)
{
stringBuilder ??= new();
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsNoArguments, optionName, provider.DisplayName, provider.Uid));
}
else if (arity < option.Arity.Min)
{
stringBuilder ??= new();
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsAtLeastArguments, optionName, provider.DisplayName, provider.Uid, option.Arity.Min));
}
else if (arity > option.Arity.Max)
{
stringBuilder ??= new();
stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsAtMostArguments, optionName, provider.DisplayName, provider.Uid, option.Arity.Max));
}
}

return stringBuilder.Length > 0
return stringBuilder?.Length > 0
? ValidationResult.Invalid(stringBuilder.ToTrimmedString())
: ValidationResult.Valid();
}
Expand Down Expand Up @@ -254,7 +301,23 @@
}

private static string ToTrimmedString(this StringBuilder stringBuilder)
#pragma warning disable RS0030 // Do not use banned APIs
=> stringBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray());
#pragma warning restore RS0030 // Do not use banned APIs
{
// Use a more efficient approach to trim without creating unnecessary intermediate strings
string result = stringBuilder.ToString();
int end = result.Length;

// Find the last non-whitespace char
while (end > 0)
{
char c = result[end - 1];
if (c is not ('\r' or '\n'))
{
break;
}

end--;
}

return end == result.Length ? result : result.Substring(0, end);
}
}
Loading