Path: blob/master/test/langtools/jdk/jshell/CommandCompletionTest.java
64440 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 8144095 8164825 8169818 8153402 8165405 8177079 8178013 8167554 8166232 827732826* @summary Test Command Completion27* @modules jdk.compiler/com.sun.tools.javac.api28* jdk.compiler/com.sun.tools.javac.main29* jdk.jdeps/com.sun.tools.javap30* jdk.jshell/jdk.internal.jshell.tool31* @library /tools/lib32* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask33* @build ReplToolTesting TestingInputStream Compiler34* @run testng CommandCompletionTest35*/3637import java.io.IOException;38import java.nio.file.FileSystems;39import java.nio.file.Files;40import java.nio.file.Path;41import java.nio.file.Paths;42import java.util.Arrays;43import java.util.Collections;44import java.util.List;45import java.util.Locale;46import java.util.function.Predicate;47import java.util.stream.Collectors;48import java.util.stream.Stream;49import java.util.stream.StreamSupport;5051import org.testng.SkipException;52import org.testng.annotations.Test;5354import jdk.internal.jshell.tool.JShellTool;55import jdk.internal.jshell.tool.JShellToolBuilder;56import jdk.jshell.SourceCodeAnalysis.Suggestion;5758import static org.testng.Assert.assertEquals;59import static org.testng.Assert.assertTrue;60import static org.testng.Assert.fail;6162public class CommandCompletionTest extends ReplToolTesting {636465private JShellTool repl;6667@Override68protected void testRawRun(Locale locale, String[] args) {69repl = ((JShellToolBuilder) builder(locale))70.rawTool();71try {72repl.start(args);73} catch (Exception ex) {74fail("Repl tool died with exception", ex);75}76}7778public void assertCompletion(boolean after, String code, boolean isSmart, String... expected) {79if (!after) {80setCommandInput("\n");81} else {82assertCompletion(code, isSmart, expected);83}84}8586public void assertCompletion(String code, boolean isSmart, String... expected) {87List<String> completions = computeCompletions(code, isSmart);88assertEquals(completions, Arrays.asList(expected), "Command: " + code + ", output: " +89completions.toString());90}9192private List<String> computeCompletions(String code, boolean isSmart) {93int cursor = code.indexOf('|');94code = code.replace("|", "");95assertTrue(cursor > -1, "'|' not found: " + code);96List<Suggestion> completions =97repl.commandCompletionSuggestions(code, cursor, new int[] {-1}); //XXX: ignoring anchor for now98return completions.stream()99.filter(s -> isSmart == s.matchesType())100.map(s -> s.continuation())101.distinct()102.collect(Collectors.toList());103}104105@Test106public void testCommand() {107testNoStartUp(108a -> assertCompletion(a, "/deb|", false),109a -> assertCompletion(a, "/re|", false, "/reload ", "/reset "),110a -> assertCompletion(a, "/h|", false, "/help ", "/history ")111);112}113114@Test115public void testList() {116test(false, new String[] {"--no-startup"},117a -> assertCompletion(a, "/l|", false, "/list "),118a -> assertCompletion(a, "/list |", false, "-all", "-history", "-start "),119a -> assertCompletion(a, "/list -h|", false, "-history"),120a -> assertCompletion(a, "/list q|", false),121a -> assertVariable(a, "int", "xray"),122a -> assertCompletion(a, "/list |", false, "-all", "-history", "-start ", "1 ", "xray "),123a -> assertCompletion(a, "/list x|", false, "xray "),124a -> assertCompletion(a, "/list xray |", false)125);126}127128@Test129public void testHistory() {130test(false, new String[] {"--no-startup"},131a -> assertCompletion(a, "/hi|", false, "/history "),132a -> assertCompletion(a, "/history |", false, "-all")133);134}135136@Test137public void testDrop() {138test(false, new String[] {"--no-startup"},139a -> assertCompletion(a, "/d|", false, "/drop "),140a -> assertClass(a, "class cTest {}", "class", "cTest"),141a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"),142a -> assertVariable(a, "int", "fTest"),143a -> assertCompletion(a, "/drop |", false, "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "),144a -> assertCompletion(a, "/drop f|", false, "fTest ")145);146}147148@Test149public void testEdit() {150test(false, new String[]{"--no-startup"},151a -> assertCompletion(a, "/e|", false, "/edit ", "/env ", "/exit "),152a -> assertCompletion(a, "/ed|", false, "/edit "),153a -> assertClass(a, "class cTest {}", "class", "cTest"),154a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"),155a -> assertVariable(a, "int", "fTest"),156a -> assertCompletion(a, "/edit |", false,157"-all" , "-start " , "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "),158a -> assertCompletion(a, "/edit cTest |", false,159"2 ", "3 ", "fTest ", "mTest "),160a -> assertCompletion(a, "/edit 1 fTest |", false,161"2 ", "mTest "),162a -> assertCompletion(a, "/edit f|", false, "fTest "),163a -> assertCompletion(a, "/edit mTest f|", false, "fTest ")164);165}166167@Test168public void testHelp() {169testNoStartUp(170a -> assertCompletion(a, "/help |", false,171"/! ", "/-<n> ", "/<id> ", "/? ", "/drop ",172"/edit ", "/env ", "/exit ",173"/help ", "/history ", "/imports ",174"/list ", "/methods ", "/open ", "/reload ", "/reset ",175"/save ", "/set ", "/types ", "/vars ", "context ",176"id ", "intro ", "keys ", "rerun ", "shortcuts "),177a -> assertCompletion(a, "/? |", false,178"/! ", "/-<n> ", "/<id> ", "/? ", "/drop ",179"/edit ", "/env ", "/exit ",180"/help ", "/history ", "/imports ",181"/list ", "/methods ", "/open ", "/reload ", "/reset ",182"/save ", "/set ", "/types ", "/vars ", "context ",183"id ", "intro ", "keys ", "rerun ", "shortcuts "),184a -> assertCompletion(a, "/help /s|", false,185"/save ", "/set "),186a -> assertCompletion(a, "/help /set |", false,187"editor", "feedback", "format", "indent", "mode", "prompt", "start", "truncation"),188a -> assertCompletion(a, "/help set |", false,189"editor", "feedback", "format", "indent", "mode", "prompt", "start", "truncation"),190a -> assertCompletion(a, "/help /edit |", false),191a -> assertCompletion(a, "/help dr|", false,192"drop ")193);194}195196@Test197public void testReload() {198String[] ropts = new String[] { "-add-exports ", "-add-modules ",199"-class-path ", "-module-path ", "-quiet ", "-restore " };200String[] dropts = new String[] { "--add-exports ", "--add-modules ",201"--class-path ", "--module-path ", "--quiet ", "--restore " };202testNoStartUp(203a -> assertCompletion(a, "/reloa |", false, ropts),204a -> assertCompletion(a, "/relo |", false, ropts),205a -> assertCompletion(a, "/reload -|", false, ropts),206a -> assertCompletion(a, "/reload --|", false, dropts),207a -> assertCompletion(a, "/reload -restore |", false, ropts),208a -> assertCompletion(a, "/reload -restore --|", false, dropts),209a -> assertCompletion(a, "/reload -rest|", false, "-restore "),210a -> assertCompletion(a, "/reload --r|", false, "--restore "),211a -> assertCompletion(a, "/reload -q|", false, "-quiet "),212a -> assertCompletion(a, "/reload -add|", false, "-add-exports ", "-add-modules "),213a -> assertCompletion(a, "/reload -class-path . -quiet |", false, ropts)214);215}216217@Test218public void testEnv() {219String[] ropts = new String[] { "-add-exports ", "-add-modules ",220"-class-path ", "-module-path " };221String[] dropts = new String[] { "--add-exports ", "--add-modules ",222"--class-path ", "--module-path " };223testNoStartUp(224a -> assertCompletion(a, "/env |", false, ropts),225a -> assertCompletion(a, "/env -|", false, ropts),226a -> assertCompletion(a, "/env --|", false, dropts),227a -> assertCompletion(a, "/env --a|", false, "--add-exports ", "--add-modules "),228a -> assertCompletion(a, "/env -add-|", false, "-add-exports ", "-add-modules "),229a -> assertCompletion(a, "/env -class-path . |", false, ropts),230a -> assertCompletion(a, "/env -class-path . --|", false, dropts)231);232}233234@Test235public void testReset() {236String[] ropts = new String[] { "-add-exports ", "-add-modules ",237"-class-path ", "-module-path " };238String[] dropts = new String[] { "--add-exports ", "--add-modules ",239"--class-path ", "--module-path " };240testNoStartUp(241a -> assertCompletion(a, "/reset |", false, ropts),242a -> assertCompletion(a, "/res -m|", false, "-module-path "),243a -> assertCompletion(a, "/res -module-|", false, "-module-path "),244a -> assertCompletion(a, "/res --m|", false, "--module-path "),245a -> assertCompletion(a, "/res --module-|", false, "--module-path "),246a -> assertCompletion(a, "/reset -add|", false, "-add-exports ", "-add-modules "),247a -> assertCompletion(a, "/rese -class-path . |", false, ropts),248a -> assertCompletion(a, "/rese -class-path . --|", false, dropts)249);250}251252@Test253public void testVarsMethodsTypes() {254testNoStartUp(255a -> assertCompletion(a, "/v|", false, "/vars "),256a -> assertCompletion(a, "/m|", false, "/methods "),257a -> assertCompletion(a, "/t|", false, "/types "),258a -> assertClass(a, "class cTest {}", "class", "cTest"),259a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"),260a -> assertVariable(a, "int", "fTest"),261a -> assertCompletion(a, "/vars |", false, "-all", "-start ", "3 ", "fTest "),262a -> assertCompletion(a, "/meth |", false, "-all", "-start ", "2 ", "mTest "),263a -> assertCompletion(a, "/typ |", false, "-all", "-start ", "1 ", "cTest "),264a -> assertCompletion(a, "/var f|", false, "fTest ")265);266}267268@Test269public void testOpen() throws IOException {270Compiler compiler = new Compiler();271testNoStartUp(272a -> assertCompletion(a, "/o|", false, "/open ")273);274List<String> p1 = listFiles(Paths.get(""));275getRootDirectories().forEach(s -> p1.add(s.toString()));276Collections.sort(p1);277testNoStartUp(278a -> assertCompletion(a, "/open |", false, p1.toArray(new String[p1.size()]))279);280Path classDir = compiler.getClassDir();281List<String> p2 = listFiles(classDir);282testNoStartUp(283a -> assertCompletion(a, "/open " + classDir + "/|", false, p2.toArray(new String[p2.size()]))284);285}286287@Test288public void testSave() throws IOException {289Compiler compiler = new Compiler();290testNoStartUp(291a -> assertCompletion(a, "/s|", false, "/save ", "/set ")292);293List<String> p1 = listFiles(Paths.get(""));294Collections.addAll(p1, "-all ", "-history ", "-start ");295getRootDirectories().forEach(s -> p1.add(s.toString()));296Collections.sort(p1);297testNoStartUp(298a -> assertCompletion(a, "/save |", false, p1.toArray(new String[p1.size()]))299);300Path classDir = compiler.getClassDir();301List<String> p2 = listFiles(classDir);302testNoStartUp(303a -> assertCompletion(a, "/save " + classDir + "/|",304false, p2.toArray(new String[p2.size()])),305a -> assertCompletion(a, "/save -all " + classDir + "/|",306false, p2.toArray(new String[p2.size()]))307);308}309310@Test311public void testClassPath() throws IOException {312Compiler compiler = new Compiler();313Path outDir = compiler.getPath("testClasspathCompletion");314Files.createDirectories(outDir);315Files.createDirectories(outDir.resolve("dir"));316createIfNeeded(outDir.resolve("test.jar"));317createIfNeeded(outDir.resolve("test.zip"));318compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }");319String jarName = "test.jar";320compiler.jar(outDir, jarName, "pkg/A.class");321compiler.getPath(outDir).resolve(jarName);322List<String> paths = listFiles(outDir, CLASSPATH_FILTER);323String[] pathArray = paths.toArray(new String[paths.size()]);324testNoStartUp(325a -> assertCompletion(a, "/env -class-path " + outDir + "/|", false, pathArray),326a -> assertCompletion(a, "/env --class-path " + outDir + "/|", false, pathArray),327a -> assertCompletion(a, "/env -clas " + outDir + "/|", false, pathArray),328a -> assertCompletion(a, "/env --class-p " + outDir + "/|", false, pathArray),329a -> assertCompletion(a, "/env --module-path . --class-p " + outDir + "/|", false, pathArray)330);331}332333@Test334public void testClassPathWithSpace() throws IOException {335Compiler compiler = new Compiler();336Path outDir = compiler.getPath("testClassPathWithSpace");337Path dirWithSpace = Files.createDirectories(outDir.resolve("dir with space"));338Files.createDirectories(dirWithSpace.resolve("nested with space"));339String[] pathArray = new String[] {"dir\\ with\\ space/"};340String[] pathArray2 = new String[] {"nested\\ with\\ space/"};341testNoStartUp(342a -> assertCompletion(a, "/env -class-path " + outDir + "/|", false, pathArray),343a -> assertCompletion(a, "/env -class-path " + outDir + "/dir|", false, pathArray),344a -> assertCompletion(a, "/env -class-path " + outDir + "/dir\\ with|", false, pathArray),345a -> assertCompletion(a, "/env -class-path " + outDir + "/dir\\ with\\ space/|", false, pathArray2)346);347}348349@Test350public void testUserHome() throws IOException {351List<String> completions;352Path home = Paths.get(System.getProperty("user.home"));353String selectedFile;354try (Stream<Path> content = Files.list(home)) {355selectedFile = content.filter(CLASSPATH_FILTER)356.filter(file -> file.getFileName().toString().contains(" "))357.findAny()358.map(file -> file.getFileName().toString().replace(" ", "\\ "))359.orElse(null);360}361if (selectedFile == null) {362throw new SkipException("No suitable file(s) found for this test in " + home);363}364try (Stream<Path> content = Files.list(home)) {365completions = content.filter(CLASSPATH_FILTER)366.filter(file -> file.getFileName().toString().startsWith(selectedFile.replace("\\ ", " ")))367.map(file -> file.getFileName().toString().replace(" ", "\\ ") + (Files.isDirectory(file) ? "/" : ""))368.sorted()369.collect(Collectors.toList());370}371testNoStartUp(372a -> assertCompletion(a, "/env --class-path ~/" + selectedFile + "|", false, completions.toArray(new String[completions.size()]))373);374}375376@Test377public void testSet() throws IOException {378List<String> p1 = listFiles(Paths.get(""));379getRootDirectories().forEach(s -> p1.add(s.toString()));380Collections.sort(p1);381382String[] modes = {"concise ", "normal ", "silent ", "verbose "};383String[] options = {"-command", "-delete", "-quiet"};384String[] modesWithOptions = Stream.concat(Arrays.stream(options), Arrays.stream(modes)).sorted().toArray(String[]::new);385test(false, new String[] {"--no-startup"},386a -> assertCompletion(a, "/se|", false, "/set "),387a -> assertCompletion(a, "/set |", false, "editor ", "feedback ", "format ", "indent ", "mode ", "prompt ", "start ", "truncation "),388389// /set editor390a -> assertCompletion(a, "/set e|", false, "editor "),391a -> assertCompletion(a, "/set editor |", false, p1.toArray(new String[p1.size()])),392393// /set feedback394a -> assertCompletion(a, "/set fe|", false, "feedback "),395a -> assertCompletion(a, "/set fe |", false, modes),396397// /set format398a -> assertCompletion(a, "/set fo|", false, "format "),399a -> assertCompletion(a, "/set fo |", false, modes),400401// /set mode402a -> assertCompletion(a, "/set mo|", false, "mode "),403a -> assertCompletion(a, "/set mo |", false),404a -> assertCompletion(a, "/set mo newmode |", false, modesWithOptions),405a -> assertCompletion(a, "/set mo newmode -|", false, options),406a -> assertCompletion(a, "/set mo newmode -command |", false),407a -> assertCompletion(a, "/set mo newmode normal |", false, options),408409// /set prompt410a -> assertCompletion(a, "/set pro|", false, "prompt "),411a -> assertCompletion(a, "/set pro |", false, modes),412413// /set start414a -> assertCompletion(a, "/set st|", false, "start "),415a -> assertCompletion(a, "/set st |", false, p1.toArray(new String[p1.size()])),416417// /set truncation418a -> assertCompletion(a, "/set tr|", false, "truncation "),419a -> assertCompletion(a, "/set tr |", false, modes)420);421}422423private void createIfNeeded(Path file) throws IOException {424if (!Files.exists(file))425Files.createFile(file);426}427private List<String> listFiles(Path path) throws IOException {428return listFiles(path, ACCEPT_ALL);429}430431private List<String> listFiles(Path path, Predicate<? super Path> filter) throws IOException {432try (Stream<Path> stream = Files.list(path)) {433return stream.filter(filter)434.map(p -> p.getFileName().toString() + (Files.isDirectory(p) ? "/" : ""))435.sorted()436.collect(Collectors.toList());437}438}439440private static final Predicate<? super Path> ACCEPT_ALL =441(file) -> !file.endsWith(".") && !file.endsWith("..");442443private static final Predicate<? super Path> CLASSPATH_FILTER =444(file) -> ACCEPT_ALL.test(file) &&445(Files.isDirectory(file) ||446file.getFileName().toString().endsWith(".jar") ||447file.getFileName().toString().endsWith(".zip"));448449private static Iterable<? extends Path> getRootDirectories() {450return StreamSupport.stream(FileSystems.getDefault()451.getRootDirectories()452.spliterator(),453false)454.filter(p -> Files.exists(p))455.collect(Collectors.toList());456}457}458459460