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 8151754 8080883 8160089 8170162 8166581 8172102 8171343 8178023 8186708 8179856
  26  * @summary Testing start-up options.
  27  * @modules jdk.compiler/com.sun.tools.javac.api
  28  *          jdk.compiler/com.sun.tools.javac.main
  29  *          jdk.jdeps/com.sun.tools.javap
  30  *          jdk.jshell/jdk.internal.jshell.tool
  31  * @library /tools/lib
  32  * @build Compiler toolbox.ToolBox
  33  * @run testng StartOptionTest
  34  */
  35 
  36 import java.io.ByteArrayInputStream;
  37 import java.io.ByteArrayOutputStream;
  38 import java.io.InputStream;
  39 import java.io.PrintStream;
  40 import java.nio.charset.StandardCharsets;
  41 import java.nio.file.Path;
  42 import java.util.HashMap;
  43 import java.util.Locale;
  44 import java.util.function.Consumer;
  45 
  46 import java.util.logging.Level;
  47 import java.util.logging.Logger;
  48 import org.testng.annotations.AfterMethod;
  49 import org.testng.annotations.BeforeMethod;
  50 import org.testng.annotations.Test;
  51 import jdk.jshell.tool.JavaShellToolBuilder;
  52 import static org.testng.Assert.assertEquals;
  53 import static org.testng.Assert.assertFalse;
  54 import static org.testng.Assert.assertTrue;
  55 import static org.testng.Assert.fail;
  56 
  57 @Test
  58 public class StartOptionTest {
  59 
  60     private ByteArrayOutputStream cmdout;
  61     private ByteArrayOutputStream cmderr;
  62     private ByteArrayOutputStream console;
  63     private ByteArrayOutputStream userout;
  64     private ByteArrayOutputStream usererr;
  65     private InputStream cmdInStream;
  66 
  67     private JavaShellToolBuilder builder() {
  68         // turn on logging of launch failures
  69         Logger.getLogger("jdk.jshell.execution").setLevel(Level.ALL);
  70         return JavaShellToolBuilder
  71                     .builder()
  72                     .out(new PrintStream(cmdout), new PrintStream(console), new PrintStream(userout))
  73                     .err(new PrintStream(cmderr), new PrintStream(usererr))
  74                     .in(cmdInStream, null)
  75                     .persistence(new HashMap<>())
  76                     .env(new HashMap<>())
  77                     .locale(Locale.ROOT);
  78     }
  79 
  80     private void runShell(String... args) {
  81         try {
  82             builder()
  83                     .run(args);
  84         } catch (Exception ex) {
  85             fail("Repl tool died with exception", ex);
  86         }
  87     }
  88 
  89     protected void check(ByteArrayOutputStream str, Consumer<String> checkOut, String label) {
  90         byte[] bytes = str.toByteArray();
  91         str.reset();
  92         String out =  new String(bytes, StandardCharsets.UTF_8);
  93         if (checkOut != null) {
  94             checkOut.accept(out);
  95         } else {
  96             assertEquals("", out, label + ": Expected empty -- ");
  97         }
  98     }
  99 
 100     protected void start(Consumer<String> checkCmdOutput,
 101             Consumer<String> checkUserOutput, Consumer<String> checkError,
 102             String... args) throws Exception {
 103         runShell(args);
 104         check(cmdout, checkCmdOutput, "cmdout");
 105         check(cmderr, checkError, "cmderr");
 106         check(console, null, "console");
 107         check(userout, checkUserOutput, "userout");
 108         check(usererr, null, "usererr");
 109     }
 110 
 111     protected void start(String expectedCmdOutput, String expectedError, String... args) throws Exception {
 112         startWithUserOutput(expectedCmdOutput, "",  expectedError, args);
 113     }
 114 
 115     private void startWithUserOutput(String expectedCmdOutput, String expectedUserOutput,
 116             String expectedError, String... args) throws Exception {
 117         start(
 118                 s -> assertEquals(s.trim(), expectedCmdOutput, "cmdout: "),
 119                 s -> assertEquals(s.trim(), expectedUserOutput, "userout: "),
 120                 s -> assertEquals(s.trim(), expectedError, "cmderr: "),
 121                 args);
 122     }
 123 
 124     @BeforeMethod
 125     public void setUp() {
 126         cmdout  = new ByteArrayOutputStream();
 127         cmderr  = new ByteArrayOutputStream();
 128         console = new ByteArrayOutputStream();
 129         userout = new ByteArrayOutputStream();
 130         usererr = new ByteArrayOutputStream();
 131         cmdInStream = new ByteArrayInputStream("/exit\n".getBytes());
 132     }
 133 
 134     protected String writeToFile(String stuff) throws Exception {
 135         Compiler compiler = new Compiler();
 136         Path p = compiler.getPath("doit.repl");
 137         compiler.writeToFile(p, stuff);
 138         return p.toString();
 139     }
 140 
 141     public void testCommandFile() throws Exception {
 142         String fn = writeToFile("String str = \"Hello \"\n/list\nSystem.out.println(str + str)\n/exit\n");
 143         startWithUserOutput("1 : String str = \"Hello \";", "Hello Hello", "", "--no-startup", fn, "-s");
 144     }
 145 
 146     public void testUsage() throws Exception {
 147         for (String opt : new String[]{"-h", "--help"}) {
 148             start(s -> {
 149                 assertTrue(s.split("\n").length >= 7, "Not enough usage lines: " + s);
 150                 assertTrue(s.startsWith("Usage:   jshell <option>..."), "Unexpect usage start: " + s);
 151                 assertTrue(s.contains("--show-version"), "Expected help: " + s);
 152                 assertFalse(s.contains("Welcome"), "Unexpected start: " + s);
 153             }, null, null, opt);
 154         }
 155     }
 156 
 157     public void testHelpExtra() throws Exception {
 158         for (String opt : new String[]{"-X", "--help-extra"}) {
 159             start(s -> {
 160                 assertTrue(s.split("\n").length >= 5, "Not enough help-extra lines: " + s);
 161                 assertTrue(s.contains("--add-exports"), "Expected --add-exports: " + s);
 162                 assertTrue(s.contains("--execution"), "Expected --add-exports: " + s);
 163                 assertFalse(s.contains("Welcome"), "Unexpected start: " + s);
 164             }, null, null, opt);
 165         }
 166     }
 167 
 168     public void testUnknown() throws Exception {
 169         start(null, null,
 170               s -> assertEquals(s.trim(), "Unknown option: u"), "-unknown");
 171         start(null, null,
 172               s -> assertEquals(s.trim(), "Unknown option: unknown"), "--unknown");
 173     }
 174 
 175     /**
 176      * Test that input is read with "-" and there is no extra output.
 177      * @throws Exception
 178      */
 179     public void testHypenFile() throws Exception {
 180         cmdInStream = new ByteArrayInputStream("System.out.print(\"Hello\");\n".getBytes());
 181         startWithUserOutput("", "Hello", "", "-");
 182         cmdInStream = new ByteArrayInputStream("System.out.print(\"Hello\");\n".getBytes());
 183         startWithUserOutput("", "Hello", "", "-", "-");
 184         Compiler compiler = new Compiler();
 185         Path path = compiler.getPath("markload.jsh");
 186         compiler.writeToFile(path, "System.out.print(\"===\");");
 187         cmdInStream = new ByteArrayInputStream("System.out.print(\"Hello\");\n".getBytes());
 188         startWithUserOutput("", "===Hello===", "", path.toString(), "-", path.toString());
 189         // check that errors go to standard error
 190         cmdInStream = new ByteArrayInputStream(") Foobar".getBytes());
 191         start(
 192                 s -> assertEquals(s.trim(), "", "cmdout: empty"),
 193                 s -> assertEquals(s.trim(), "", "userout: empty"),
 194                 s -> assertTrue(s.contains("illegal start of expression"),
 195                             "cmderr: illegal start of expression"),
 196                 "-");
 197     }
 198 
 199     /**
 200      * Test that non-existent load file sends output to stderr and does not start (no welcome).
 201      * @throws Exception
 202      */
 203     public void testUnknownLoadFile() throws Exception {
 204         start("", "File 'UNKNOWN' for 'jshell' is not found.", "UNKNOWN");
 205     }
 206 
 207     public void testStartup() throws Exception {
 208         Compiler compiler = new Compiler();
 209         Path p = compiler.getPath("file.txt");
 210         compiler.writeToFile(p);
 211         start("", "Argument to startup missing.", "--startup");
 212         start("", "Conflicting options: both --startup and --no-startup were used.", "--no-startup", "--startup", p.toString());
 213         start("", "Conflicting options: both --startup and --no-startup were used.", "--startup", p.toString(), "--no-startup");
 214         start("", "Argument to startup missing.", "--no-startup", "--startup");
 215     }
 216 
 217     public void testStartupFailedOption() throws Exception {
 218         start(
 219                 s -> assertEquals(s.trim(), "", "cmdout: "),
 220                 s -> assertEquals(s.trim(), "", "userout: "),
 221                 s -> assertTrue(s.contains("Unrecognized option: -hoge-foo-bar"), "cmderr: " + s),
 222                 "-R-hoge-foo-bar");
 223     }
 224 
 225     public void testStartupUnknown() throws Exception {
 226         start("", "File 'UNKNOWN' for '--startup' is not found.", "--startup", "UNKNOWN");
 227         start("", "File 'UNKNOWN' for '--startup' is not found.", "--startup", "DEFAULT", "--startup", "UNKNOWN");
 228     }
 229 
 230     public void testClasspath() throws Exception {
 231         for (String cp : new String[] {"--class-path"}) {
 232             start("", "Only one --class-path option may be used.", cp, ".", "--class-path", ".");
 233             start("", "Argument to class-path missing.", cp);
 234         }
 235     }
 236 
 237     public void testUnknownModule() throws Exception {
 238         start(
 239                 s -> assertEquals(s.trim(), "", "cmdout: "),
 240                 s -> assertEquals(s.trim(), "", "userout: "),
 241                 s -> assertTrue(s.contains("rror") && s.contains("unKnown"), "cmderr: " + s),
 242                 "--add-modules", "unKnown");
 243     }
 244 
 245     public void testFeedbackOptionConflict() throws Exception {
 246         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.",
 247                 "--feedback", "concise", "--feedback", "verbose");
 248         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "--feedback", "concise", "-s");
 249         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "--feedback", "verbose", "-q");
 250         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "--feedback", "concise", "-v");
 251         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-v", "--feedback", "concise");
 252         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-q", "-v");
 253         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-s", "-v");
 254         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-v", "-q");
 255         start("", "Only one feedback option (--feedback, -q, -s, or -v) may be used.", "-q", "-s");
 256     }
 257 
 258     public void testNegFeedbackOption() throws Exception {
 259         start("", "Argument to feedback missing.", "--feedback");
 260         start("", "Does not match any current feedback mode: blorp -- --feedback blorp", "--feedback", "blorp");
 261     }
 262 
 263     public void testVersion() throws Exception {
 264         start(
 265                 s -> {
 266                     assertTrue(s.startsWith("jshell"), "unexpected version: " + s);
 267                     assertFalse(s.contains("Welcome"), "Unexpected start: " + s);
 268                 },
 269                 null, null,
 270                 "--version");
 271     }
 272 
 273     public void testShowVersion() throws Exception {
 274         runShell("--show-version");
 275         check(cmdout,
 276                 s -> {
 277                     assertTrue(s.startsWith("jshell"), "unexpected version: " + s);
 278                     assertTrue(s.contains("Welcome"), "Expected start (but got no welcome): " + s);
 279                 },
 280                 "cmdout");
 281         check(cmderr, null, "cmderr");
 282         check(console,
 283                 s -> assertTrue(s.trim().startsWith("jshell>"), "Expected prompt, got: " + s),
 284                 "console");
 285         check(userout, null, "userout");
 286         check(usererr, null, "usererr");
 287     }
 288 
 289     @AfterMethod
 290     public void tearDown() {
 291         cmdout  = null;
 292         cmderr  = null;
 293         console = null;
 294         userout = null;
 295         usererr = null;
 296         cmdInStream = null;
 297     }
 298 }