From ed3965c19d2a44bb9e273c3b6b413f145a879cbe Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Mon, 13 May 2024 13:54:39 -0700 Subject: [PATCH] Clean up location parser & suggestions, add more tests (#61) * Add failing test for location flag suggestions * Adjust LocationParser * Add more location suggestion tests * fix formatting * remove test command --- .../location/LocationCoordinateParser.java | 7 +- .../parser/location/LocationParser.java | 35 ++++++-- .../bukkit/parser/LocationArgumentTest.java | 88 ++++++++++++++++--- 3 files changed, 104 insertions(+), 26 deletions(-) diff --git a/cloud-bukkit/src/main/java/org/incendo/cloud/bukkit/parser/location/LocationCoordinateParser.java b/cloud-bukkit/src/main/java/org/incendo/cloud/bukkit/parser/location/LocationCoordinateParser.java index 75e15a26..6c20e744 100644 --- a/cloud-bukkit/src/main/java/org/incendo/cloud/bukkit/parser/location/LocationCoordinateParser.java +++ b/cloud-bukkit/src/main/java/org/incendo/cloud/bukkit/parser/location/LocationCoordinateParser.java @@ -61,11 +61,8 @@ public final class LocationCoordinateParser implements ArgumentParser implements ArgumentParser, Blo } else if (bukkitSender instanceof Entity) { originalLocation = ((Entity) bukkitSender).getLocation(); } else { - originalLocation = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + if (Bukkit.getWorlds().isEmpty()) { + // For testing + originalLocation = new Location(null, 0, 0, 0); + } else { + originalLocation = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + } } if (((coordinates[0].type() == LocationCoordinateType.LOCAL) @@ -216,19 +221,33 @@ private static float toRadians(final float degrees) { final @NonNull CommandContext commandContext, final @NonNull CommandInput input ) { - final int toSkip = Math.min(components, input.remainingTokens()) - 1; - final StringBuilder prefix = new StringBuilder(); - for (int i = 0; i < toSkip; i++) { - prefix.append(input.readStringSkipWhitespace()).append(" "); + final CommandInput inputCopy = input.copy(); + + int idx = input.cursor(); + for (int i = 0; i < components; i++) { + idx = input.cursor(); + if (!input.hasRemainingInput(true)) { + break; + } + final ArgumentParseResult coordinateResult = new LocationCoordinateParser().parse( + commandContext, + input + ); + if (coordinateResult.failure().isPresent()) { + break; + } } + input.cursor(idx); if (input.hasRemainingInput() && (input.peek() == '~' || input.peek() == '^')) { - prefix.append(input.read()); + input.read(); } + final String prefix = inputCopy.difference(input, true); + return IntegerParser.getSuggestions( - SUGGESTION_RANGE, - input + SUGGESTION_RANGE, + input ).stream().map(string -> prefix + string).collect(Collectors.toList()); } diff --git a/cloud-bukkit/src/test/java/org/incendo/cloud/bukkit/parser/LocationArgumentTest.java b/cloud-bukkit/src/test/java/org/incendo/cloud/bukkit/parser/LocationArgumentTest.java index 5b6f4749..00b01dcf 100644 --- a/cloud-bukkit/src/test/java/org/incendo/cloud/bukkit/parser/LocationArgumentTest.java +++ b/cloud-bukkit/src/test/java/org/incendo/cloud/bukkit/parser/LocationArgumentTest.java @@ -23,17 +23,30 @@ // package org.incendo.cloud.bukkit.parser; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.util.Vector; import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.BukkitCommandContextKeys; import org.incendo.cloud.bukkit.parser.location.LocationParser; import org.incendo.cloud.bukkit.util.ServerTest; import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.internal.CommandRegistrationHandler; import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.flag.CommandFlag; +import org.incendo.cloud.suggestion.Suggestion; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -41,6 +54,7 @@ import org.mockito.Mock; import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.Mockito.when; @@ -49,6 +63,54 @@ class LocationArgumentTest extends ServerTest { @Mock private World world; + @ParameterizedTest + @MethodSource + void suggestions(final String input, final List expectedSuggestions) { + final LocationParser parser = new LocationParser<>(); + + final Set suggestions = + StreamSupport.stream(parser.suggestionProvider() + .suggestionsFuture(this.commandContext(), CommandInput.of(input)).join().spliterator(), false) + .map(Suggestion::suggestion) + .collect(Collectors.toSet()); + + assertEquals(new HashSet<>(expectedSuggestions), suggestions); + } + + @ParameterizedTest + @MethodSource("suggestions") + void suggestionsFlag(final String input, final List expectedSuggestions) { + final CommandManager manager = new CommandManager( + ExecutionCoordinator.simpleCoordinator(), CommandRegistrationHandler.nullCommandRegistrationHandler() + ) { + @Override + public boolean hasPermission(@NonNull CommandSender sender, @NonNull String permission) { + return true; + } + }; + manager.registerCommandPreProcessor(ctx -> ctx.commandContext().store(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER, ctx.commandContext().sender())); + manager.command( + manager.commandBuilder("flag") + .flag(CommandFlag.builder("loc").withComponent(LocationParser.locationParser()).build()) + ); + final Function flagSuggestion = s -> "flag --loc " + s; + assertEquals( + new HashSet<>(expectedSuggestions), + manager.suggestionFactory().suggestImmediately(this.commandContext().sender(), flagSuggestion.apply(input)) + .list().stream().map(Suggestion::suggestion).collect(Collectors.toSet()) + ); + } + + static Stream suggestions() { + return Stream.of( + arguments("", Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")), + arguments("1 ", Arrays.asList("1 0", "1 1", "1 2", "1 3", "1 4", "1 5", "1 6", "1 7", "1 8", "1 9")), + arguments("1 1", Arrays.asList("1 10", "1 11", "1 12", "1 13", "1 14", "1 15", "1 16", "1 17", "1 18", "1 19")), + arguments("1 1 ", Arrays.asList("1 1 0", "1 1 1", "1 1 2", "1 1 3", "1 1 4", "1 1 5", "1 1 6", "1 1 7", "1 1 8", "1 1 9")), + arguments("1 1 1", Arrays.asList("1 1 1", "1 1 10", "1 1 11", "1 1 12", "1 1 13", "1 1 14", "1 1 15", "1 1 16", "1 1 17", "1 1 18", "1 1 19")) + ); + } + @ParameterizedTest @MethodSource("Parse_HappyFlow_Success_Source") void Parse_HappyFlow_Success(final @NonNull String input, final @NonNull Vector expectedLocation) { @@ -59,8 +121,8 @@ void Parse_HappyFlow_Success(final @NonNull String input, final @NonNull Vector // Act final ArgumentParseResult result = parser.parse( - this.commandContext(), - commandInput + this.commandContext(), + commandInput ); // Assert @@ -72,19 +134,19 @@ void Parse_HappyFlow_Success(final @NonNull String input, final @NonNull Vector static @NonNull Stream<@NonNull Arguments> Parse_HappyFlow_Success_Source() { return Stream.of( - arguments("~ ~ ~", new Vector(0, 0, 0)), - arguments("~10 ~10 ~10", new Vector(10, 10, 10)), - arguments("~-10 ~-10 ~-10", new Vector(-10, -10, -10)), - arguments("^ ^ ^", new Vector(0, 0, 0)), - arguments("^0 ^0 ^0", new Vector(0, 0, 0)), - arguments("0 0 0", new Vector(0, 0, 0)), - arguments("10 10 10", new Vector(10, 10, 10)), - arguments("-10 -10 -10", new Vector(-10, -10, -10)) + arguments("~ ~ ~", new Vector(0, 0, 0)), + arguments("~10 ~10 ~10", new Vector(10, 10, 10)), + arguments("~-10 ~-10 ~-10", new Vector(-10, -10, -10)), + arguments("^ ^ ^", new Vector(0, 0, 0)), + arguments("^0 ^0 ^0", new Vector(0, 0, 0)), + arguments("0 0 0", new Vector(0, 0, 0)), + arguments("10 10 10", new Vector(10, 10, 10)), + arguments("-10 -10 -10", new Vector(-10, -10, -10)) ); } @ParameterizedTest - @ValueSource(strings = { "0 0", "not a location" }) + @ValueSource(strings = {"0 0", "not a location"}) void Parse_InvalidLocation_Failure(final @NonNull String input) { // Arrange final LocationParser parser = new LocationParser<>(); @@ -92,8 +154,8 @@ void Parse_InvalidLocation_Failure(final @NonNull String input) { // Act final ArgumentParseResult result = parser.parse( - this.commandContext(), - commandInput + this.commandContext(), + commandInput ); // Assert