diff --git a/gradle/changelog/sort_cli_commands.yaml b/gradle/changelog/sort_cli_commands.yaml new file mode 100644 index 0000000000..daacd492aa --- /dev/null +++ b/gradle/changelog/sort_cli_commands.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Sort CLI commands alphabetically ([#2020](https://github.com/scm-manager/scm-manager/pull/2020)) diff --git a/scm-webapp/src/main/java/sonia/scm/cli/CommandRegistry.java b/scm-webapp/src/main/java/sonia/scm/cli/CommandRegistry.java index 9eebc248ce..d05b59856f 100644 --- a/scm-webapp/src/main/java/sonia/scm/cli/CommandRegistry.java +++ b/scm-webapp/src/main/java/sonia/scm/cli/CommandRegistry.java @@ -26,10 +26,11 @@ package sonia.scm.cli; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.stream.Collectors; @Singleton public class CommandRegistry { @@ -41,17 +42,17 @@ public class CommandRegistry { this.commandCollector = commandCollector; } - public Set createCommandTree() { - Set rootCommands = new HashSet<>(); - Set registeredCommands = commandCollector.collect(); + public List createCommandTree() { + List rootCommands = new ArrayList<>(); + List sortedCommands = collectSortedCommands(); Map, RegisteredCommandNode> commandNodes = new HashMap<>(); - for (RegisteredCommand command : registeredCommands) { + for (RegisteredCommand command : sortedCommands) { commandNodes.put(command.getCommand(), new RegisteredCommandNode(command.getName(), command.getCommand())); } - for (RegisteredCommand command : registeredCommands) { + for (RegisteredCommand command : sortedCommands) { RegisteredCommandNode node = commandNodes.get(command.getCommand()); if (command.getParent() == null) { rootCommands.add(node); @@ -66,4 +67,11 @@ public class CommandRegistry { } return rootCommands; } + + private List collectSortedCommands() { + return commandCollector.collect() + .stream() + .sorted((a, b) -> a.getName().compareToIgnoreCase(b.getName())) + .collect(Collectors.toList()); + } } diff --git a/scm-webapp/src/test/java/sonia/scm/cli/CliProcessorTest.java b/scm-webapp/src/test/java/sonia/scm/cli/CliProcessorTest.java index f3cc5c342d..98760ec965 100644 --- a/scm-webapp/src/test/java/sonia/scm/cli/CliProcessorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/cli/CliProcessorTest.java @@ -24,6 +24,7 @@ package sonia.scm.cli; +import com.google.common.collect.ImmutableList; import com.google.inject.Guice; import com.google.inject.Injector; import org.junit.jupiter.api.BeforeEach; @@ -73,7 +74,7 @@ class CliProcessorTest { @Test void shouldExecutePingCommand() { - when(registry.createCommandTree()).thenReturn(Collections.singleton(new RegisteredCommandNode("ping", PingCommand.class))); + when(registry.createCommandTree()).thenReturn(ImmutableList.of(new RegisteredCommandNode("ping", PingCommand.class))); Injector injector = Guice.createInjector(); CliProcessor cliProcessor = new CliProcessor(registry, injector, exceptionHandlerFactory); @@ -84,7 +85,7 @@ class CliProcessorTest { @Test void shouldExecutePingCommandWithExitCode0() { - when(registry.createCommandTree()).thenReturn(Collections.singleton(new RegisteredCommandNode("ping", PingCommand.class))); + when(registry.createCommandTree()).thenReturn(ImmutableList.of(new RegisteredCommandNode("ping", PingCommand.class))); Injector injector = Guice.createInjector(); CliProcessor cliProcessor = new CliProcessor(registry, injector, exceptionHandlerFactory); @@ -164,7 +165,7 @@ class CliProcessorTest { two.getChildren().add(three); one.getChildren().add(two); - when(registry.createCommandTree()).thenReturn(Collections.singleton(one)); + when(registry.createCommandTree()).thenReturn(ImmutableList.of(one)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); when(context.getStdout()).thenReturn(new PrintWriter(baos)); diff --git a/scm-webapp/src/test/java/sonia/scm/cli/CommandRegistryTest.java b/scm-webapp/src/test/java/sonia/scm/cli/CommandRegistryTest.java index 813b36b5c6..7bea80be16 100644 --- a/scm-webapp/src/test/java/sonia/scm/cli/CommandRegistryTest.java +++ b/scm-webapp/src/test/java/sonia/scm/cli/CommandRegistryTest.java @@ -31,10 +31,8 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Arrays; import java.util.Collection; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @@ -52,7 +50,7 @@ class CommandRegistryTest { void shouldCreateTreeWithOnlyRootNodes() { mockCommands(rc(Object.class), rc(String.class), rc(Integer.class)); - Set commandTree = registry.createCommandTree(); + List commandTree = registry.createCommandTree(); assertContainsCommands(commandTree, Object.class, String.class, Integer.class); } @@ -60,7 +58,7 @@ class CommandRegistryTest { void shouldCreateTreeWithParents() { mockCommands(rc(Object.class), rc(String.class, Object.class), rc(Integer.class, Object.class)); - Set commandTree = registry.createCommandTree(); + List commandTree = registry.createCommandTree(); assertContainsCommands(commandTree, Object.class); assertContainsCommands(commandTree.iterator().next().getChildren(), Integer.class, String.class); @@ -70,7 +68,7 @@ class CommandRegistryTest { void shouldCreateTreeWithParentsSecondLevel() { mockCommands(rc(Object.class), rc(String.class, Object.class), rc(Integer.class, String.class)); - Set commandTree = registry.createCommandTree(); + List commandTree = registry.createCommandTree(); assertContainsCommands(commandTree, Object.class); RegisteredCommandNode rootNode = commandTree.iterator().next(); @@ -78,6 +76,18 @@ class CommandRegistryTest { assertContainsCommands(rootNode.getChildren().get(0).getChildren(), Integer.class); } + @Test + void shouldSortCommandsAlphabetically() { + mockCommands(rc(Object.class), rc(String.class, Object.class), rc(Float.class, Object.class), rc(Integer.class, Object.class)); + + List commandTree = registry.createCommandTree(); + + List subCommands = commandTree.get(0).getChildren(); + assertThat(subCommands.get(0).getCommand()).isEqualTo(Float.class); + assertThat(subCommands.get(1).getCommand()).isEqualTo(Integer.class); + assertThat(subCommands.get(2).getCommand()).isEqualTo(String.class); + } + private void mockCommands(RegisteredCommand... commands) { when(commandCollector.collect()).thenReturn(ImmutableSet.copyOf(commands)); }