Skip to content
Merged
Show file tree
Hide file tree
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
29 changes: 18 additions & 11 deletions src/Cli/dotnet/Commands/Test/TestingPlatformCommand.Help.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public IEnumerable<Action<HelpContext>> CustomHelpLayout()
return;
}

Dictionary<bool, List<CommandLineOption>> allOptions = GetAllOptions();
Dictionary<bool, List<CommandLineOption>> allOptions = GetAllOptions(context.Command.Options);
allOptions.TryGetValue(true, out List<CommandLineOption> builtInOptions);
allOptions.TryGetValue(false, out List<CommandLineOption> nonBuiltInOptions);

Expand Down Expand Up @@ -128,29 +128,36 @@ private void OnHelpRequested(object sender, HelpEventArgs args)
(isBuiltIn, value) => [.. value, (moduleName, nonBuiltInOptions.ToArray())]);
}

private Dictionary<bool, List<CommandLineOption>> GetAllOptions()
private Dictionary<bool, List<CommandLineOption>> GetAllOptions(IList<Option> commandOptions)
{
Dictionary<bool, List<CommandLineOption>> builtInToOptions = [];
Dictionary<bool, List<CommandLineOption>> filteredOptions = [];

// Create a set of option names from the command's options for efficient lookup
var commandOptionNames = commandOptions.Select(o => o.Name.TrimStart('-')).ToHashSet(StringComparer.OrdinalIgnoreCase);

foreach (KeyValuePair<string, CommandLineOption> option in _commandLineOptionNameToModuleNames)
{
if (!builtInToOptions.TryGetValue(option.Value.IsBuiltIn.Value, out List<CommandLineOption> value))
{
builtInToOptions.Add(option.Value.IsBuiltIn.Value, [option.Value]);
}
else
// Only include options that are NOT already present in the command's options
if (!commandOptionNames.Contains(option.Value.Name))
{
value.Add(option.Value);
if (!filteredOptions.TryGetValue(option.Value.IsBuiltIn.Value, out List<CommandLineOption> value))
{
filteredOptions.Add(option.Value.IsBuiltIn.Value, [option.Value]);
}
else
{
value.Add(option.Value);
}
}
}

// Sort options alphabetically by name
foreach (var optionsList in builtInToOptions.Values)
foreach (var optionsList in filteredOptions.Values)
{
optionsList.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase));
}

return builtInToOptions;
return filteredOptions;
}

private static Dictionary<bool, List<(string[], string[])>> GetModulesToMissingOptions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,55 @@ public void RunHelpOnMultipleTestProjects_ShouldReturnExitCodeSuccess(string con

result.ExitCode.Should().Be(ExitCodes.Success);
}

[InlineData(TestingConstants.Debug)]
[InlineData(TestingConstants.Release)]
[Theory]
public void RunHelpCommand_ShouldNotShowDuplicateOptions(string configuration)
{
TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectSolutionWithTestsAndArtifacts", Guid.NewGuid().ToString()).WithSource();

CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false)
.WithWorkingDirectory(testInstance.Path)
.Execute(TestingPlatformOptions.HelpOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration);

// Parse the help output to extract option names
var helpOutput = result.StdOut;

// Check for specific options we care about
string outputOptionName = TestingPlatformOptions.OutputOption.Name; // --output
string noAnsiOptionName = TestingPlatformOptions.NoAnsiOption.Name; // --no-ansi

// Count occurrences of each option in the help output
int outputOptionCount = CountOptionOccurrences(helpOutput!, outputOptionName);
int noAnsiOptionCount = CountOptionOccurrences(helpOutput!, noAnsiOptionName);

// Assert that each option appears only once
outputOptionCount.Should().Be(1, $"Option '{outputOptionName}' should not appear more than once in help output");
noAnsiOptionCount.Should().Be(1, $"Option '{noAnsiOptionName}' should not appear more than once in help output");

result.ExitCode.Should().Be(ExitCodes.Success);
}

private static int CountOptionOccurrences(string helpOutput, string optionName)
{
// Split by lines and look for lines that start with the option (accounting for indentation)
var lines = helpOutput.Split('\n', StringSplitOptions.RemoveEmptyEntries);
int count = 0;

foreach (var line in lines)
{
var trimmedLine = line.Trim();
// Look for lines that start with the option name (e.g., "--output" or "--no-ansi")
if (trimmedLine.StartsWith(optionName, StringComparison.OrdinalIgnoreCase))
{
count++;
}
}

return count;
}


}
}