| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ignite.internal.cli.core.repl.completer; |
| |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.hamcrest.Matchers.both; |
| import static org.hamcrest.Matchers.contains; |
| import static org.hamcrest.Matchers.containsInAnyOrder; |
| import static org.hamcrest.Matchers.empty; |
| import static org.hamcrest.Matchers.hasSize; |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.Matchers.not; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| import org.apache.ignite.internal.cli.commands.Options; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.DisplayName; |
| import org.junit.jupiter.api.Test; |
| |
| class DynamicCompleterRegistryTest { |
| |
| DynamicCompleterRegistry registry; |
| |
| DynamicCompleter completer1; |
| |
| DynamicCompleter completer2; |
| |
| DynamicCompleter completer3; |
| |
| /** Makes reading easier. */ |
| private static String[] words(String... words) { |
| return words; |
| } |
| |
| @BeforeEach |
| void setUp() { |
| registry = new DynamicCompleterRegistry(); |
| completer1 = words -> null; |
| completer2 = words -> null; |
| completer3 = words -> null; |
| } |
| |
| @Test |
| void findsCompleterRegisteredWithMultyCommand() { |
| // Given |
| registry.register( |
| CompleterConf.builder() |
| .command("command1") |
| .command("command2").build(), |
| words -> completer1 |
| ); |
| |
| // Then |
| assertThat(registry.findCompleters(words("command1")), containsInAnyOrder(completer1)); |
| // And |
| assertThat(registry.findCompleters(words("command2")), containsInAnyOrder(completer1)); |
| } |
| |
| @Test |
| void findsCompletersRegisteredWithStaticCommand() { |
| // Given |
| registry.register(CompleterConf.forCommand("command1", "subcommand1"), words -> completer1); |
| registry.register(CompleterConf.forCommand("command1", "subcommand1"), words -> completer2); |
| registry.register(CompleterConf.forCommand("command2"), words -> completer3); |
| |
| // When find completers for "command1" |
| List<DynamicCompleter> completers1 = registry.findCompleters(words("command1", "subcommand1")); |
| |
| // Then |
| assertThat(completers1, containsInAnyOrder(completer1, completer2)); |
| // And |
| List<DynamicCompleter> completers2 = registry.findCompleters(words("command2")); |
| assertThat(completers2, contains(completer3)); |
| } |
| |
| @Test |
| void returnsEmptyCollectionIfThereIsNoSuitableCompleter() { |
| // Given |
| registry.register(CompleterConf.forCommand("command1", "subcommand1"), words -> completer1); |
| registry.register(CompleterConf.forCommand("command1", "subcommand1"), words -> completer2); |
| registry.register(CompleterConf.forCommand("command2"), words -> completer3); |
| |
| // Then |
| assertThat(registry.findCompleters(words("command1")), is(empty())); |
| assertThat(registry.findCompleters(words("command3")), is(empty())); |
| // But if command start with one of the registered prefixes |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "subsubcommand1")), |
| containsInAnyOrder(completer1, completer2) |
| ); |
| } |
| |
| @Test |
| void doesntReturnCompleterWithDisableOptions() { |
| // Given |
| registry.register( |
| CompleterConf.builder().command("command1", "subcommand1").disableOptions("--stopWord").build(), |
| words -> completer1 |
| ); |
| |
| // Then |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "subsub1")), |
| containsInAnyOrder(completer1) |
| ); |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "subsub1", "")), |
| containsInAnyOrder(completer1) |
| ); |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "", "")), |
| containsInAnyOrder(completer1) |
| ); |
| |
| // But if command ends with a stop word |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "--stopWord")), |
| is(empty()) |
| ); |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "--stopWord", "")), |
| is(empty()) |
| ); |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "--stopWord", "do-not-complete-me")), |
| is(empty()) |
| ); |
| assertThat( |
| registry.findCompleters(words("command1", "subcommand1", "--stopWord", "do-not-complete-me", "but-complete-me")), |
| containsInAnyOrder(completer1) |
| ); |
| } |
| |
| @Test |
| @DisplayName("If enable options are provided than they are taken into account") |
| void enableOptions() { |
| // Given completer with enable option |
| registry.register( |
| CompleterConf.builder() |
| .command("command1") |
| .enableOptions("--complete-after-me", "-com") |
| .build(), |
| words -> completer1 |
| ); |
| |
| // Then for words without those options there are no completers |
| assertThat(registry.findCompleters(words("command1")), is(empty())); |
| // And enable option should not be completed itself |
| assertThat(registry.findCompleters(words("command1", "-co")), is(empty())); |
| |
| // But if any of the enable options are provided then completer is found |
| assertThat(registry.findCompleters(words("command1", "some", "--complete-after-me", "")), containsInAnyOrder(completer1)); |
| assertThat(registry.findCompleters(words("command1", "-com", "")), containsInAnyOrder(completer1)); |
| assertThat(registry.findCompleters(words("command1", "-com", " ")), containsInAnyOrder(completer1)); |
| assertThat(registry.findCompleters(words("command1", "-com", "autocompleteme")), containsInAnyOrder(completer1)); |
| } |
| |
| @Test |
| @DisplayName("Combination of completers with the same enable/disable options") |
| void combinationOfCompleters() { |
| // Given first completer with disable option |
| registry.register( |
| CompleterConf.builder() |
| .command("node", "config", "show") |
| .disableOptions("-n").build(), |
| words -> completer1 |
| ); |
| // And the second completer with the same enable option |
| registry.register( |
| CompleterConf.builder().enableOptions("-n").build(), |
| (words -> completer2) |
| ); |
| |
| // Then they do not intersect |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n")), // "-n" is competed by completer2 |
| both(hasSize(1)).and(containsInAnyOrder(completer2)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "nodeName")), |
| both(hasSize(1)).and(containsInAnyOrder(completer2)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "nodeName", " ")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "nodeName", "completeMe")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| } |
| |
| @Test |
| @DisplayName("If some completer has exclusive enable option set then all other completers have this option as disable option") |
| void exclusiveEnableOption() { |
| // Given completer with exclusive enable option -n |
| registry.register( |
| CompleterConf.builder() |
| .command("node", "config", "show") |
| .enableOptions(Options.NODE_NAME) |
| .exclusiveEnableOptions().build(), |
| words -> completer1 |
| ); |
| // And completer for the same command |
| registry.register( |
| CompleterConf.forCommand("node", "config", "show"), |
| words -> completer2 |
| ); |
| // And common completer for other option |
| registry.register( |
| CompleterConf.builder().enableOptions("-l").build(), |
| words -> completer3 |
| ); |
| |
| // Then exclusive option is on the priority and no other completer is used for -n |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "nod")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| // But other cases work well |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-l", "nod")), |
| both(hasSize(2)).and(containsInAnyOrder(completer2, completer3)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "node", " ")), |
| both(hasSize(1)).and(containsInAnyOrder(completer2)) |
| ); |
| } |
| |
| @Test |
| @DisplayName( |
| "If some completer has exclusive enable option set then all other completers have this option as disable option, " |
| + "registration order changed" |
| ) |
| void exclusiveEnableOptionRegisterInAnotherOrder() { |
| // Given completers without exclusive enable option registered first |
| registry.register( |
| CompleterConf.forCommand("node", "config", "show"), |
| words -> completer2 |
| ); |
| registry.register( |
| CompleterConf.builder().enableOptions("-l").build(), |
| words -> completer3 |
| ); |
| // And exclusiveEnableOption is registered last |
| registry.register( |
| CompleterConf.builder() |
| .enableOptions("-n") |
| .exclusiveEnableOptions().build(), |
| words -> completer1 |
| ); |
| |
| // Then exclusive option is on the priority and no other completer is used for -n |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "nod")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n")), |
| both(hasSize(1)).and(containsInAnyOrder(completer1)) |
| ); |
| // But other cases work well |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-l", "nod")), |
| both(hasSize(2)).and(containsInAnyOrder(completer2, completer3)) |
| ); |
| assertThat( |
| registry.findCompleters(words("node", "config", "show", "-n", "node", "")), |
| both(hasSize(1)).and(containsInAnyOrder(completer2)) |
| ); |
| } |
| |
| @Test |
| void doesNotReturnFilteredCandidates() { |
| // Given |
| registry.register(CompleterConf.builder() |
| .command("command1", "subcommand1") |
| .filter((words, candidates) -> { |
| return Arrays.stream(candidates) |
| .filter(it -> !"candidate2".equals(it)) |
| .toArray(String[]::new); |
| }).build(), words -> ignored -> List.of("candidate1", "candidate2")); |
| |
| // Then |
| List<DynamicCompleter> completers = registry.findCompleters(words("command1", "subcommand1")); |
| assertThat(completers, not(empty())); |
| |
| List<String> candidates = completers.stream() |
| .flatMap(it -> it.complete(words("word")).stream()) |
| .collect(Collectors.toList()); |
| assertThat(candidates, contains("candidate1")); |
| assertThat(candidates, not(contains("candidate2"))); |
| } |
| |
| @Test |
| void positionalParameter() { |
| // Given completer for single positional parameter |
| registry.register( |
| CompleterConf.builder() |
| .command("cluster", "config", "show") // should be real commands |
| .singlePositionalParameter() |
| .build(), |
| words -> completer1 |
| ); |
| |
| // When there is no positional argument typed |
| List<DynamicCompleter> completers = registry.findCompleters(words("cluster", "config", "show")); |
| |
| // Then completer is returned |
| assertThat(completers, contains(completer1)); |
| |
| // When there is one positional argument typed |
| List<DynamicCompleter> completersWithPositional = registry.findCompleters(words("cluster", "config", "show", "arg1", "")); |
| |
| // Then completer is not returned |
| assertThat(completersWithPositional, is(empty())); |
| } |
| |
| @Test |
| void sameOptionDifferentCompleters() { |
| // Given |
| registry.register( |
| CompleterConf.builder() |
| .command("command", "subcommand1") |
| .enableOptions("--to") |
| .exclusiveEnableOptions().build(), |
| words -> completer1 |
| ); |
| |
| registry.register( |
| CompleterConf.builder() |
| .command("command", "subcommand2") |
| .enableOptions("--to") |
| .exclusiveEnableOptions().build(), |
| words -> completer2); |
| |
| // Then |
| assertThat(registry.findCompleters(words("command", "subcommand1", "--to")), containsInAnyOrder(completer1)); |
| // And |
| assertThat(registry.findCompleters(words("command", "subcommand2", "--to")), containsInAnyOrder(completer2)); |
| } |
| |
| @Test |
| void positionParameterIsNotCollapsedWithOptions() { |
| registry.register( |
| CompleterConf.builder() |
| .enableOptions(Options.NODE_NAME) |
| .exclusiveEnableOptions().build(), |
| words -> completer1 |
| ); |
| |
| registry.register( |
| CompleterConf.builder() |
| .command("cluster", "config", "show") |
| .command("cluster", "config", "update") |
| .singlePositionalParameter().build(), |
| words -> completer2 |
| ); |
| |
| // Then |
| assertThat( |
| registry.findCompleters(words("cluster", "config", "update", Options.NODE_NAME.fullName(), "")), |
| hasSize(1) |
| ); |
| // And |
| assertThat( |
| registry.findCompleters(words("cluster", "config", "update", "")), |
| hasSize(1) |
| ); |
| } |
| } |