1/* 2 * Copyright (c) 2015, 2017, 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 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 38import java.io.IOException; 39import java.nio.file.FileSystems; 40import java.nio.file.Files; 41import java.nio.file.Path; 42import java.nio.file.Paths; 43import java.util.Arrays; 44import java.util.Collections; 45import java.util.List; 46import java.util.Locale; 47import java.util.function.Predicate; 48import java.util.stream.Collectors; 49import java.util.stream.Stream; 50import java.util.stream.StreamSupport; 51 52import org.testng.annotations.Test; 53import jdk.internal.jshell.tool.JShellTool; 54import jdk.internal.jshell.tool.JShellToolBuilder; 55import jdk.jshell.SourceCodeAnalysis.Suggestion; 56import static org.testng.Assert.assertEquals; 57import static org.testng.Assert.assertTrue; 58import static org.testng.Assert.fail; 59 60public class CommandCompletionTest extends ReplToolTesting { 61 62 63 private JShellTool repl; 64 65 @Override 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 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 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 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 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 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 127 public void testDrop() { 128 test(false, new String[] {"--no-startup"}, 129 a -> assertCompletion(a, "/d|", false, "/drop "), 130 a -> assertClass(a, "class cTest {}", "class", "cTest"), 131 a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), 132 a -> assertVariable(a, "int", "fTest"), 133 a -> assertCompletion(a, "/drop |", false, "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "), 134 a -> assertCompletion(a, "/drop f|", false, "fTest ") 135 ); 136 } 137 138 @Test 139 public void testEdit() { 140 test(false, new String[]{"--no-startup"}, 141 a -> assertCompletion(a, "/e|", false, "/edit ", "/env ", "/exit "), 142 a -> assertCompletion(a, "/ed|", false, "/edit "), 143 a -> assertClass(a, "class cTest {}", "class", "cTest"), 144 a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), 145 a -> assertVariable(a, "int", "fTest"), 146 a -> assertCompletion(a, "/edit |", false, 147 "-all" , "-start " , "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "), 148 a -> assertCompletion(a, "/edit cTest |", false, 149 "2 ", "3 ", "fTest ", "mTest "), 150 a -> assertCompletion(a, "/edit 1 fTest |", false, 151 "2 ", "mTest "), 152 a -> assertCompletion(a, "/edit f|", false, "fTest "), 153 a -> assertCompletion(a, "/edit mTest f|", false, "fTest ") 154 ); 155 } 156 157 @Test 158 public void testHelp() { 159 testNoStartUp( 160 a -> assertCompletion(a, "/help |", false, 161 "/! ", "/-<n> ", "/<id> ", "/? ", "/drop ", 162 "/edit ", "/env ", "/exit ", 163 "/help ", "/history ", "/imports ", 164 "/list ", "/methods ", "/open ", "/reload ", "/reset ", 165 "/save ", "/set ", "/types ", "/vars ", "context ", "intro ", "rerun ", "shortcuts "), 166 a -> assertCompletion(a, "/? |", false, 167 "/! ", "/-<n> ", "/<id> ", "/? ", "/drop ", 168 "/edit ", "/env ", "/exit ", 169 "/help ", "/history ", "/imports ", 170 "/list ", "/methods ", "/open ", "/reload ", "/reset ", 171 "/save ", "/set ", "/types ", "/vars ", "context ", "intro ", "rerun ", "shortcuts "), 172 a -> assertCompletion(a, "/help /s|", false, 173 "/save ", "/set "), 174 a -> assertCompletion(a, "/help /set |", false, 175 "editor", "feedback", "format", "mode", "prompt", "start", "truncation"), 176 a -> assertCompletion(a, "/help set |", false, 177 "editor", "feedback", "format", "mode", "prompt", "start", "truncation"), 178 a -> assertCompletion(a, "/help /edit |", false), 179 a -> assertCompletion(a, "/help dr|", false, 180 "drop ") 181 ); 182 } 183 184 @Test 185 public void testReload() { 186 String[] ropts = new String[] { "-add-exports ", "-add-modules ", 187 "-class-path ", "-module-path ", "-quiet ", "-restore " }; 188 String[] dropts = new String[] { "--add-exports ", "--add-modules ", 189 "--class-path ", "--module-path ", "--quiet ", "--restore " }; 190 testNoStartUp( 191 a -> assertCompletion(a, "/reloa |", false, ropts), 192 a -> assertCompletion(a, "/relo |", false, ropts), 193 a -> assertCompletion(a, "/reload -|", false, ropts), 194 a -> assertCompletion(a, "/reload --|", false, dropts), 195 a -> assertCompletion(a, "/reload -restore |", false, ropts), 196 a -> assertCompletion(a, "/reload -restore --|", false, dropts), 197 a -> assertCompletion(a, "/reload -rest|", false, "-restore "), 198 a -> assertCompletion(a, "/reload --r|", false, "--restore "), 199 a -> assertCompletion(a, "/reload -q|", false, "-quiet "), 200 a -> assertCompletion(a, "/reload -add|", false, "-add-exports ", "-add-modules "), 201 a -> assertCompletion(a, "/reload -class-path . -quiet |", false, ropts) 202 ); 203 } 204 205 @Test 206 public void testEnv() { 207 String[] ropts = new String[] { "-add-exports ", "-add-modules ", 208 "-class-path ", "-module-path " }; 209 String[] dropts = new String[] { "--add-exports ", "--add-modules ", 210 "--class-path ", "--module-path " }; 211 testNoStartUp( 212 a -> assertCompletion(a, "/env |", false, ropts), 213 a -> assertCompletion(a, "/env -|", false, ropts), 214 a -> assertCompletion(a, "/env --|", false, dropts), 215 a -> assertCompletion(a, "/env --a|", false, "--add-exports ", "--add-modules "), 216 a -> assertCompletion(a, "/env -add-|", false, "-add-exports ", "-add-modules "), 217 a -> assertCompletion(a, "/env -class-path . |", false, ropts), 218 a -> assertCompletion(a, "/env -class-path . --|", false, dropts) 219 ); 220 } 221 222 @Test 223 public void testReset() { 224 String[] ropts = new String[] { "-add-exports ", "-add-modules ", 225 "-class-path ", "-module-path " }; 226 String[] dropts = new String[] { "--add-exports ", "--add-modules ", 227 "--class-path ", "--module-path " }; 228 testNoStartUp( 229 a -> assertCompletion(a, "/reset |", false, ropts), 230 a -> assertCompletion(a, "/res -m|", false, "-module-path "), 231 a -> assertCompletion(a, "/res -module-|", false, "-module-path "), 232 a -> assertCompletion(a, "/res --m|", false, "--module-path "), 233 a -> assertCompletion(a, "/res --module-|", false, "--module-path "), 234 a -> assertCompletion(a, "/reset -add|", false, "-add-exports ", "-add-modules "), 235 a -> assertCompletion(a, "/rese -class-path . |", false, ropts), 236 a -> assertCompletion(a, "/rese -class-path . --|", false, dropts) 237 ); 238 } 239 240 @Test 241 public void testVarsMethodsTypes() { 242 testNoStartUp( 243 a -> assertCompletion(a, "/v|", false, "/vars "), 244 a -> assertCompletion(a, "/m|", false, "/methods "), 245 a -> assertCompletion(a, "/t|", false, "/types "), 246 a -> assertClass(a, "class cTest {}", "class", "cTest"), 247 a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), 248 a -> assertVariable(a, "int", "fTest"), 249 a -> assertCompletion(a, "/vars |", false, "-all", "-start ", "3 ", "fTest "), 250 a -> assertCompletion(a, "/meth |", false, "-all", "-start ", "2 ", "mTest "), 251 a -> assertCompletion(a, "/typ |", false, "-all", "-start ", "1 ", "cTest "), 252 a -> assertCompletion(a, "/var f|", false, "fTest ") 253 ); 254 } 255 256 @Test 257 public void testOpen() throws IOException { 258 Compiler compiler = new Compiler(); 259 testNoStartUp( 260 a -> assertCompletion(a, "/o|", false, "/open ") 261 ); 262 List<String> p1 = listFiles(Paths.get("")); 263 getRootDirectories().forEach(s -> p1.add(s.toString())); 264 Collections.sort(p1); 265 testNoStartUp( 266 a -> assertCompletion(a, "/open |", false, p1.toArray(new String[p1.size()])) 267 ); 268 Path classDir = compiler.getClassDir(); 269 List<String> p2 = listFiles(classDir); 270 testNoStartUp( 271 a -> assertCompletion(a, "/open " + classDir + "/|", false, p2.toArray(new String[p2.size()])) 272 ); 273 } 274 275 @Test 276 public void testSave() throws IOException { 277 Compiler compiler = new Compiler(); 278 testNoStartUp( 279 a -> assertCompletion(a, "/s|", false, "/save ", "/set ") 280 ); 281 List<String> p1 = listFiles(Paths.get("")); 282 Collections.addAll(p1, "-all ", "-history ", "-start "); 283 getRootDirectories().forEach(s -> p1.add(s.toString())); 284 Collections.sort(p1); 285 testNoStartUp( 286 a -> assertCompletion(a, "/save |", false, p1.toArray(new String[p1.size()])) 287 ); 288 Path classDir = compiler.getClassDir(); 289 List<String> p2 = listFiles(classDir); 290 testNoStartUp( 291 a -> assertCompletion(a, "/save " + classDir + "/|", 292 false, p2.toArray(new String[p2.size()])), 293 a -> assertCompletion(a, "/save -all " + classDir + "/|", 294 false, p2.toArray(new String[p2.size()])) 295 ); 296 } 297 298 @Test 299 public void testClassPath() throws IOException { 300 Compiler compiler = new Compiler(); 301 Path outDir = compiler.getPath("testClasspathCompletion"); 302 Files.createDirectories(outDir); 303 Files.createDirectories(outDir.resolve("dir")); 304 createIfNeeded(outDir.resolve("test.jar")); 305 createIfNeeded(outDir.resolve("test.zip")); 306 compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }"); 307 String jarName = "test.jar"; 308 compiler.jar(outDir, jarName, "pkg/A.class"); 309 compiler.getPath(outDir).resolve(jarName); 310 List<String> paths = listFiles(outDir, CLASSPATH_FILTER); 311 String[] pathArray = paths.toArray(new String[paths.size()]); 312 testNoStartUp( 313 a -> assertCompletion(a, "/env -class-path " + outDir + "/|", false, pathArray), 314 a -> assertCompletion(a, "/env --class-path " + outDir + "/|", false, pathArray), 315 a -> assertCompletion(a, "/env -clas " + outDir + "/|", false, pathArray), 316 a -> assertCompletion(a, "/env --class-p " + outDir + "/|", false, pathArray), 317 a -> assertCompletion(a, "/env --module-path . --class-p " + outDir + "/|", false, pathArray) 318 ); 319 } 320 321 @Test 322 public void testUserHome() throws IOException { 323 List<String> completions; 324 Path home = Paths.get(System.getProperty("user.home")); 325 try (Stream<Path> content = Files.list(home)) { 326 completions = content.filter(CLASSPATH_FILTER) 327 .map(file -> file.getFileName().toString() + (Files.isDirectory(file) ? "/" : "")) 328 .sorted() 329 .collect(Collectors.toList()); 330 } 331 testNoStartUp( 332 a -> assertCompletion(a, "/env --class-path ~/|", false, completions.toArray(new String[completions.size()])) 333 ); 334 } 335 336 @Test 337 public void testSet() throws IOException { 338 List<String> p1 = listFiles(Paths.get("")); 339 getRootDirectories().forEach(s -> p1.add(s.toString())); 340 Collections.sort(p1); 341 342 String[] modes = {"concise ", "normal ", "silent ", "verbose "}; 343 String[] options = {"-command", "-delete", "-quiet"}; 344 String[] modesWithOptions = Stream.concat(Arrays.stream(options), Arrays.stream(modes)).sorted().toArray(String[]::new); 345 test(false, new String[] {"--no-startup"}, 346 a -> assertCompletion(a, "/se|", false, "/set "), 347 a -> assertCompletion(a, "/set |", false, "editor ", "feedback ", "format ", "mode ", "prompt ", "start ", "truncation "), 348 349 // /set editor 350 a -> assertCompletion(a, "/set e|", false, "editor "), 351 a -> assertCompletion(a, "/set editor |", false, p1.toArray(new String[p1.size()])), 352 353 // /set feedback 354 a -> assertCompletion(a, "/set fe|", false, "feedback "), 355 a -> assertCompletion(a, "/set fe |", false, modes), 356 357 // /set format 358 a -> assertCompletion(a, "/set fo|", false, "format "), 359 a -> assertCompletion(a, "/set fo |", false, modes), 360 361 // /set mode 362 a -> assertCompletion(a, "/set mo|", false, "mode "), 363 a -> assertCompletion(a, "/set mo |", false), 364 a -> assertCompletion(a, "/set mo newmode |", false, modesWithOptions), 365 a -> assertCompletion(a, "/set mo newmode -|", false, options), 366 a -> assertCompletion(a, "/set mo newmode -command |", false), 367 a -> assertCompletion(a, "/set mo newmode normal |", false, options), 368 369 // /set prompt 370 a -> assertCompletion(a, "/set pro|", false, "prompt "), 371 a -> assertCompletion(a, "/set pro |", false, modes), 372 373 // /set start 374 a -> assertCompletion(a, "/set st|", false, "start "), 375 a -> assertCompletion(a, "/set st |", false, p1.toArray(new String[p1.size()])), 376 377 // /set truncation 378 a -> assertCompletion(a, "/set tr|", false, "truncation "), 379 a -> assertCompletion(a, "/set tr |", false, modes) 380 ); 381 } 382 383 private void createIfNeeded(Path file) throws IOException { 384 if (!Files.exists(file)) 385 Files.createFile(file); 386 } 387 private List<String> listFiles(Path path) throws IOException { 388 return listFiles(path, ACCEPT_ALL); 389 } 390 391 private List<String> listFiles(Path path, Predicate<? super Path> filter) throws IOException { 392 try (Stream<Path> stream = Files.list(path)) { 393 return stream.filter(filter) 394 .map(p -> p.getFileName().toString() + (Files.isDirectory(p) ? "/" : "")) 395 .sorted() 396 .collect(Collectors.toList()); 397 } 398 } 399 400 private static final Predicate<? super Path> ACCEPT_ALL = 401 (file) -> !file.endsWith(".") && !file.endsWith(".."); 402 403 private static final Predicate<? super Path> CLASSPATH_FILTER = 404 (file) -> ACCEPT_ALL.test(file) && 405 (Files.isDirectory(file) || 406 file.getFileName().toString().endsWith(".jar") || 407 file.getFileName().toString().endsWith(".zip")); 408 409 private static Iterable<? extends Path> getRootDirectories() { 410 return StreamSupport.stream(FileSystems.getDefault() 411 .getRootDirectories() 412 .spliterator(), 413 false) 414 .filter(p -> Files.exists(p)) 415 .collect(Collectors.toList()); 416 } 417} 418