1 /* 2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8144095 8164825 8169818 8153402 8165405 8177079 8178013 8167554 8166232 27 * @summary Test Command Completion 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.main 30 * jdk.jdeps/com.sun.tools.javap 31 * jdk.jshell/jdk.internal.jshell.tool 32 * @library /tools/lib 33 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask 34 * @build ReplToolTesting TestingInputStream Compiler 35 * @run testng CommandCompletionTest 36 */ 37 38 import java.io.IOException; 39 import java.nio.file.FileSystems; 40 import java.nio.file.Files; 41 import java.nio.file.Path; 42 import java.nio.file.Paths; 43 import java.util.Arrays; 44 import java.util.Collections; 45 import java.util.List; 46 import java.util.Locale; 47 import java.util.function.Predicate; 48 import java.util.stream.Collectors; 49 import java.util.stream.Stream; 50 import java.util.stream.StreamSupport; 51 52 import org.testng.annotations.Test; 53 import jdk.internal.jshell.tool.JShellTool; 54 import jdk.internal.jshell.tool.JShellToolBuilder; 55 import jdk.jshell.SourceCodeAnalysis.Suggestion; 56 import static org.testng.Assert.assertEquals; 57 import static org.testng.Assert.assertTrue; 58 import static org.testng.Assert.fail; 59 60 public class CommandCompletionTest extends ReplToolTesting { 61 62 63 private JShellTool repl; 64 65 @Override testRawRun(Locale locale, String[] args)66 protected void testRawRun(Locale locale, String[] args) { 67 repl = ((JShellToolBuilder) builder(locale)) 68 .rawTool(); 69 try { 70 repl.start(args); 71 } catch (Exception ex) { 72 fail("Repl tool died with exception", ex); 73 } 74 } 75 assertCompletion(boolean after, String code, boolean isSmart, String... expected)76 public void assertCompletion(boolean after, String code, boolean isSmart, String... expected) { 77 if (!after) { 78 setCommandInput("\n"); 79 } else { 80 assertCompletion(code, isSmart, expected); 81 } 82 } 83 assertCompletion(String code, boolean isSmart, String... expected)84 public void assertCompletion(String code, boolean isSmart, String... expected) { 85 List<String> completions = computeCompletions(code, isSmart); 86 assertEquals(completions, Arrays.asList(expected), "Command: " + code + ", output: " + 87 completions.toString()); 88 } 89 computeCompletions(String code, boolean isSmart)90 private List<String> computeCompletions(String code, boolean isSmart) { 91 int cursor = code.indexOf('|'); 92 code = code.replace("|", ""); 93 assertTrue(cursor > -1, "'|' not found: " + code); 94 List<Suggestion> completions = 95 repl.commandCompletionSuggestions(code, cursor, new int[] {-1}); //XXX: ignoring anchor for now 96 return completions.stream() 97 .filter(s -> isSmart == s.matchesType()) 98 .map(s -> s.continuation()) 99 .distinct() 100 .collect(Collectors.toList()); 101 } 102 103 @Test testCommand()104 public void testCommand() { 105 testNoStartUp( 106 a -> assertCompletion(a, "/deb|", false), 107 a -> assertCompletion(a, "/re|", false, "/reload ", "/reset "), 108 a -> assertCompletion(a, "/h|", false, "/help ", "/history ") 109 ); 110 } 111 112 @Test testList()113 public void testList() { 114 test(false, new String[] {"--no-startup"}, 115 a -> assertCompletion(a, "/l|", false, "/list "), 116 a -> assertCompletion(a, "/list |", false, "-all", "-history", "-start "), 117 a -> assertCompletion(a, "/list -h|", false, "-history"), 118 a -> assertCompletion(a, "/list q|", false), 119 a -> assertVariable(a, "int", "xray"), 120 a -> assertCompletion(a, "/list |", false, "-all", "-history", "-start ", "1 ", "xray "), 121 a -> assertCompletion(a, "/list x|", false, "xray "), 122 a -> assertCompletion(a, "/list xray |", false) 123 ); 124 } 125 126 @Test testHistory()127 public void testHistory() { 128 test(false, new String[] {"--no-startup"}, 129 a -> assertCompletion(a, "/hi|", false, "/history "), 130 a -> assertCompletion(a, "/history |", false, "-all") 131 ); 132 } 133 134 @Test testDrop()135 public void testDrop() { 136 test(false, new String[] {"--no-startup"}, 137 a -> assertCompletion(a, "/d|", false, "/drop "), 138 a -> assertClass(a, "class cTest {}", "class", "cTest"), 139 a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), 140 a -> assertVariable(a, "int", "fTest"), 141 a -> assertCompletion(a, "/drop |", false, "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "), 142 a -> assertCompletion(a, "/drop f|", false, "fTest ") 143 ); 144 } 145 146 @Test testEdit()147 public void testEdit() { 148 test(false, new String[]{"--no-startup"}, 149 a -> assertCompletion(a, "/e|", false, "/edit ", "/env ", "/exit "), 150 a -> assertCompletion(a, "/ed|", false, "/edit "), 151 a -> assertClass(a, "class cTest {}", "class", "cTest"), 152 a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), 153 a -> assertVariable(a, "int", "fTest"), 154 a -> assertCompletion(a, "/edit |", false, 155 "-all" , "-start " , "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "), 156 a -> assertCompletion(a, "/edit cTest |", false, 157 "2 ", "3 ", "fTest ", "mTest "), 158 a -> assertCompletion(a, "/edit 1 fTest |", false, 159 "2 ", "mTest "), 160 a -> assertCompletion(a, "/edit f|", false, "fTest "), 161 a -> assertCompletion(a, "/edit mTest f|", false, "fTest ") 162 ); 163 } 164 165 @Test testHelp()166 public void testHelp() { 167 testNoStartUp( 168 a -> assertCompletion(a, "/help |", false, 169 "/! ", "/-<n> ", "/<id> ", "/? ", "/drop ", 170 "/edit ", "/env ", "/exit ", 171 "/help ", "/history ", "/imports ", 172 "/list ", "/methods ", "/open ", "/reload ", "/reset ", 173 "/save ", "/set ", "/types ", "/vars ", "context ", 174 "id ", "intro ", "keys ", "rerun ", "shortcuts "), 175 a -> assertCompletion(a, "/? |", false, 176 "/! ", "/-<n> ", "/<id> ", "/? ", "/drop ", 177 "/edit ", "/env ", "/exit ", 178 "/help ", "/history ", "/imports ", 179 "/list ", "/methods ", "/open ", "/reload ", "/reset ", 180 "/save ", "/set ", "/types ", "/vars ", "context ", 181 "id ", "intro ", "keys ", "rerun ", "shortcuts "), 182 a -> assertCompletion(a, "/help /s|", false, 183 "/save ", "/set "), 184 a -> assertCompletion(a, "/help /set |", false, 185 "editor", "feedback", "format", "mode", "prompt", "start", "truncation"), 186 a -> assertCompletion(a, "/help set |", false, 187 "editor", "feedback", "format", "mode", "prompt", "start", "truncation"), 188 a -> assertCompletion(a, "/help /edit |", false), 189 a -> assertCompletion(a, "/help dr|", false, 190 "drop ") 191 ); 192 } 193 194 @Test testReload()195 public void testReload() { 196 String[] ropts = new String[] { "-add-exports ", "-add-modules ", 197 "-class-path ", "-module-path ", "-quiet ", "-restore " }; 198 String[] dropts = new String[] { "--add-exports ", "--add-modules ", 199 "--class-path ", "--module-path ", "--quiet ", "--restore " }; 200 testNoStartUp( 201 a -> assertCompletion(a, "/reloa |", false, ropts), 202 a -> assertCompletion(a, "/relo |", false, ropts), 203 a -> assertCompletion(a, "/reload -|", false, ropts), 204 a -> assertCompletion(a, "/reload --|", false, dropts), 205 a -> assertCompletion(a, "/reload -restore |", false, ropts), 206 a -> assertCompletion(a, "/reload -restore --|", false, dropts), 207 a -> assertCompletion(a, "/reload -rest|", false, "-restore "), 208 a -> assertCompletion(a, "/reload --r|", false, "--restore "), 209 a -> assertCompletion(a, "/reload -q|", false, "-quiet "), 210 a -> assertCompletion(a, "/reload -add|", false, "-add-exports ", "-add-modules "), 211 a -> assertCompletion(a, "/reload -class-path . -quiet |", false, ropts) 212 ); 213 } 214 215 @Test testEnv()216 public void testEnv() { 217 String[] ropts = new String[] { "-add-exports ", "-add-modules ", 218 "-class-path ", "-module-path " }; 219 String[] dropts = new String[] { "--add-exports ", "--add-modules ", 220 "--class-path ", "--module-path " }; 221 testNoStartUp( 222 a -> assertCompletion(a, "/env |", false, ropts), 223 a -> assertCompletion(a, "/env -|", false, ropts), 224 a -> assertCompletion(a, "/env --|", false, dropts), 225 a -> assertCompletion(a, "/env --a|", false, "--add-exports ", "--add-modules "), 226 a -> assertCompletion(a, "/env -add-|", false, "-add-exports ", "-add-modules "), 227 a -> assertCompletion(a, "/env -class-path . |", false, ropts), 228 a -> assertCompletion(a, "/env -class-path . --|", false, dropts) 229 ); 230 } 231 232 @Test testReset()233 public void testReset() { 234 String[] ropts = new String[] { "-add-exports ", "-add-modules ", 235 "-class-path ", "-module-path " }; 236 String[] dropts = new String[] { "--add-exports ", "--add-modules ", 237 "--class-path ", "--module-path " }; 238 testNoStartUp( 239 a -> assertCompletion(a, "/reset |", false, ropts), 240 a -> assertCompletion(a, "/res -m|", false, "-module-path "), 241 a -> assertCompletion(a, "/res -module-|", false, "-module-path "), 242 a -> assertCompletion(a, "/res --m|", false, "--module-path "), 243 a -> assertCompletion(a, "/res --module-|", false, "--module-path "), 244 a -> assertCompletion(a, "/reset -add|", false, "-add-exports ", "-add-modules "), 245 a -> assertCompletion(a, "/rese -class-path . |", false, ropts), 246 a -> assertCompletion(a, "/rese -class-path . --|", false, dropts) 247 ); 248 } 249 250 @Test testVarsMethodsTypes()251 public void testVarsMethodsTypes() { 252 testNoStartUp( 253 a -> assertCompletion(a, "/v|", false, "/vars "), 254 a -> assertCompletion(a, "/m|", false, "/methods "), 255 a -> assertCompletion(a, "/t|", false, "/types "), 256 a -> assertClass(a, "class cTest {}", "class", "cTest"), 257 a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), 258 a -> assertVariable(a, "int", "fTest"), 259 a -> assertCompletion(a, "/vars |", false, "-all", "-start ", "3 ", "fTest "), 260 a -> assertCompletion(a, "/meth |", false, "-all", "-start ", "2 ", "mTest "), 261 a -> assertCompletion(a, "/typ |", false, "-all", "-start ", "1 ", "cTest "), 262 a -> assertCompletion(a, "/var f|", false, "fTest ") 263 ); 264 } 265 266 @Test testOpen()267 public void testOpen() throws IOException { 268 Compiler compiler = new Compiler(); 269 testNoStartUp( 270 a -> assertCompletion(a, "/o|", false, "/open ") 271 ); 272 List<String> p1 = listFiles(Paths.get("")); 273 getRootDirectories().forEach(s -> p1.add(s.toString())); 274 Collections.sort(p1); 275 testNoStartUp( 276 a -> assertCompletion(a, "/open |", false, p1.toArray(new String[p1.size()])) 277 ); 278 Path classDir = compiler.getClassDir(); 279 List<String> p2 = listFiles(classDir); 280 testNoStartUp( 281 a -> assertCompletion(a, "/open " + classDir + "/|", false, p2.toArray(new String[p2.size()])) 282 ); 283 } 284 285 @Test testSave()286 public void testSave() throws IOException { 287 Compiler compiler = new Compiler(); 288 testNoStartUp( 289 a -> assertCompletion(a, "/s|", false, "/save ", "/set ") 290 ); 291 List<String> p1 = listFiles(Paths.get("")); 292 Collections.addAll(p1, "-all ", "-history ", "-start "); 293 getRootDirectories().forEach(s -> p1.add(s.toString())); 294 Collections.sort(p1); 295 testNoStartUp( 296 a -> assertCompletion(a, "/save |", false, p1.toArray(new String[p1.size()])) 297 ); 298 Path classDir = compiler.getClassDir(); 299 List<String> p2 = listFiles(classDir); 300 testNoStartUp( 301 a -> assertCompletion(a, "/save " + classDir + "/|", 302 false, p2.toArray(new String[p2.size()])), 303 a -> assertCompletion(a, "/save -all " + classDir + "/|", 304 false, p2.toArray(new String[p2.size()])) 305 ); 306 } 307 308 @Test testClassPath()309 public void testClassPath() throws IOException { 310 Compiler compiler = new Compiler(); 311 Path outDir = compiler.getPath("testClasspathCompletion"); 312 Files.createDirectories(outDir); 313 Files.createDirectories(outDir.resolve("dir")); 314 createIfNeeded(outDir.resolve("test.jar")); 315 createIfNeeded(outDir.resolve("test.zip")); 316 compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }"); 317 String jarName = "test.jar"; 318 compiler.jar(outDir, jarName, "pkg/A.class"); 319 compiler.getPath(outDir).resolve(jarName); 320 List<String> paths = listFiles(outDir, CLASSPATH_FILTER); 321 String[] pathArray = paths.toArray(new String[paths.size()]); 322 testNoStartUp( 323 a -> assertCompletion(a, "/env -class-path " + outDir + "/|", false, pathArray), 324 a -> assertCompletion(a, "/env --class-path " + outDir + "/|", false, pathArray), 325 a -> assertCompletion(a, "/env -clas " + outDir + "/|", false, pathArray), 326 a -> assertCompletion(a, "/env --class-p " + outDir + "/|", false, pathArray), 327 a -> assertCompletion(a, "/env --module-path . --class-p " + outDir + "/|", false, pathArray) 328 ); 329 } 330 331 @Test testUserHome()332 public void testUserHome() throws IOException { 333 List<String> completions; 334 Path home = Paths.get(System.getProperty("user.home")); 335 try (Stream<Path> content = Files.list(home)) { 336 completions = content.filter(CLASSPATH_FILTER) 337 .map(file -> file.getFileName().toString() + (Files.isDirectory(file) ? "/" : "")) 338 .sorted() 339 .collect(Collectors.toList()); 340 } 341 testNoStartUp( 342 a -> assertCompletion(a, "/env --class-path ~/|", false, completions.toArray(new String[completions.size()])) 343 ); 344 } 345 346 @Test testSet()347 public void testSet() throws IOException { 348 List<String> p1 = listFiles(Paths.get("")); 349 getRootDirectories().forEach(s -> p1.add(s.toString())); 350 Collections.sort(p1); 351 352 String[] modes = {"concise ", "normal ", "silent ", "verbose "}; 353 String[] options = {"-command", "-delete", "-quiet"}; 354 String[] modesWithOptions = Stream.concat(Arrays.stream(options), Arrays.stream(modes)).sorted().toArray(String[]::new); 355 test(false, new String[] {"--no-startup"}, 356 a -> assertCompletion(a, "/se|", false, "/set "), 357 a -> assertCompletion(a, "/set |", false, "editor ", "feedback ", "format ", "mode ", "prompt ", "start ", "truncation "), 358 359 // /set editor 360 a -> assertCompletion(a, "/set e|", false, "editor "), 361 a -> assertCompletion(a, "/set editor |", false, p1.toArray(new String[p1.size()])), 362 363 // /set feedback 364 a -> assertCompletion(a, "/set fe|", false, "feedback "), 365 a -> assertCompletion(a, "/set fe |", false, modes), 366 367 // /set format 368 a -> assertCompletion(a, "/set fo|", false, "format "), 369 a -> assertCompletion(a, "/set fo |", false, modes), 370 371 // /set mode 372 a -> assertCompletion(a, "/set mo|", false, "mode "), 373 a -> assertCompletion(a, "/set mo |", false), 374 a -> assertCompletion(a, "/set mo newmode |", false, modesWithOptions), 375 a -> assertCompletion(a, "/set mo newmode -|", false, options), 376 a -> assertCompletion(a, "/set mo newmode -command |", false), 377 a -> assertCompletion(a, "/set mo newmode normal |", false, options), 378 379 // /set prompt 380 a -> assertCompletion(a, "/set pro|", false, "prompt "), 381 a -> assertCompletion(a, "/set pro |", false, modes), 382 383 // /set start 384 a -> assertCompletion(a, "/set st|", false, "start "), 385 a -> assertCompletion(a, "/set st |", false, p1.toArray(new String[p1.size()])), 386 387 // /set truncation 388 a -> assertCompletion(a, "/set tr|", false, "truncation "), 389 a -> assertCompletion(a, "/set tr |", false, modes) 390 ); 391 } 392 createIfNeeded(Path file)393 private void createIfNeeded(Path file) throws IOException { 394 if (!Files.exists(file)) 395 Files.createFile(file); 396 } listFiles(Path path)397 private List<String> listFiles(Path path) throws IOException { 398 return listFiles(path, ACCEPT_ALL); 399 } 400 listFiles(Path path, Predicate<? super Path> filter)401 private List<String> listFiles(Path path, Predicate<? super Path> filter) throws IOException { 402 try (Stream<Path> stream = Files.list(path)) { 403 return stream.filter(filter) 404 .map(p -> p.getFileName().toString() + (Files.isDirectory(p) ? "/" : "")) 405 .sorted() 406 .collect(Collectors.toList()); 407 } 408 } 409 410 private static final Predicate<? super Path> ACCEPT_ALL = 411 (file) -> !file.endsWith(".") && !file.endsWith(".."); 412 413 private static final Predicate<? super Path> CLASSPATH_FILTER = 414 (file) -> ACCEPT_ALL.test(file) && 415 (Files.isDirectory(file) || 416 file.getFileName().toString().endsWith(".jar") || 417 file.getFileName().toString().endsWith(".zip")); 418 getRootDirectories()419 private static Iterable<? extends Path> getRootDirectories() { 420 return StreamSupport.stream(FileSystems.getDefault() 421 .getRootDirectories() 422 .spliterator(), 423 false) 424 .filter(p -> Files.exists(p)) 425 .collect(Collectors.toList()); 426 } 427 } 428