diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index f046ed4111a..a40c27bbf47 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -237,10 +237,11 @@ public final class System { private static volatile Console cons; /** - * Returns the unique {@link java.io.Console Console} object associated + * Returns the unique {@link Console Console} object associated * with the current Java virtual machine, if any. * * @return The system console, if any, otherwise {@code null}. + * @see Console * * @since 1.6 */ diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java index 365f6d1e68a..4e4751b264e 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java @@ -50,7 +50,7 @@ public class JdkConsoleProviderImpl implements JdkConsoleProvider { */ @Override public JdkConsole console(boolean isTTY, Charset inCharset, Charset outCharset) { - return new LazyDelegatingJdkConsoleImpl(inCharset, outCharset); + return isTTY ? new LazyDelegatingJdkConsoleImpl(inCharset, outCharset) : null; } private static class LazyDelegatingJdkConsoleImpl implements JdkConsole { diff --git a/test/jdk/java/io/Console/DefaultCharsetTest.java b/test/jdk/java/io/Console/DefaultCharsetTest.java index 0fca8a3cc3f..981d92ce282 100644 --- a/test/jdk/java/io/Console/DefaultCharsetTest.java +++ b/test/jdk/java/io/Console/DefaultCharsetTest.java @@ -21,33 +21,66 @@ * questions. */ -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import java.nio.file.Files; +import java.nio.file.Paths; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static jdk.test.lib.Utils.*; /** * @test - * @bug 8341975 8351435 + * @bug 8341975 8351435 8361613 * @summary Tests the default charset. It should honor `stdout.encoding` * which should be the same as System.out.charset() - * @modules jdk.internal.le - * @run junit/othervm -Djdk.console=jdk.internal.le -Dstdout.encoding=UTF-8 DefaultCharsetTest - * @run junit/othervm -Djdk.console=jdk.internal.le -Dstdout.encoding=ISO-8859-1 DefaultCharsetTest - * @run junit/othervm -Djdk.console=jdk.internal.le -Dstdout.encoding=US-ASCII DefaultCharsetTest - * @run junit/othervm -Djdk.console=jdk.internal.le -Dstdout.encoding=foo DefaultCharsetTest - * @run junit/othervm -Djdk.console=jdk.internal.le DefaultCharsetTest + * @requires (os.family == "linux") | (os.family == "mac") + * @library /test/lib + * @build jdk.test.lib.Utils + * jdk.test.lib.JDKToolFinder + * jdk.test.lib.process.ProcessTools + * @run junit DefaultCharsetTest */ public class DefaultCharsetTest { - @Test - public void testDefaultCharset() { + @BeforeAll + static void checkExpectAvailability() { + // check "expect" command availability + var expect = Paths.get("/usr/bin/expect"); + Assumptions.assumeTrue(Files.exists(expect) && Files.isExecutable(expect), + "'" + expect + "' not found. Test ignored."); + } + @ParameterizedTest + @ValueSource(strings = {"UTF-8", "ISO-8859-1", "US-ASCII", "foo", ""}) + void testDefaultCharset(String stdoutEncoding) throws Exception { + // invoking "expect" command + OutputAnalyzer oa = ProcessTools.executeProcess( + "expect", + "-n", + TEST_SRC + "/defaultCharset.exp", + TEST_CLASSES, + TEST_JDK + "/bin/java", + "-Dstdout.encoding=" + stdoutEncoding, + getClass().getName()); + oa.reportDiagnosticSummary(); + oa.shouldHaveExitValue(0); + } + + public static void main(String... args) { var stdoutEncoding = System.getProperty("stdout.encoding"); var sysoutCharset = System.out.charset(); var consoleCharset = System.console().charset(); - System.out.println(""" - stdout.encoding = %s - System.out.charset() = %s - System.console().charset() = %s - """.formatted(stdoutEncoding, sysoutCharset.name(), consoleCharset.name())); - assertEquals(consoleCharset, sysoutCharset, - "Charsets for System.out and Console differ for stdout.encoding: %s".formatted(stdoutEncoding)); + System.out.printf(""" + stdout.encoding = %s + System.out.charset() = %s + System.console().charset() = %s + """, stdoutEncoding, sysoutCharset.name(), consoleCharset.name()); + if (!consoleCharset.equals(sysoutCharset)) { + System.err.printf("Charsets for System.out and Console differ for stdout.encoding: %s%n", stdoutEncoding); + System.exit(-1); + } } } diff --git a/test/jdk/java/io/Console/LocaleTest.java b/test/jdk/java/io/Console/LocaleTest.java index 1cab84a9af7..e9a281749b1 100644 --- a/test/jdk/java/io/Console/LocaleTest.java +++ b/test/jdk/java/io/Console/LocaleTest.java @@ -21,28 +21,40 @@ * questions. */ -import java.io.File; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.function.Predicate; +import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +import static jdk.test.lib.Utils.*; /** * @test - * @bug 8330276 8351435 + * @bug 8330276 8351435 8361613 * @summary Tests Console methods that have Locale as an argument + * @requires (os.family == "linux") | (os.family == "mac") * @library /test/lib - * @modules jdk.internal.le jdk.localedata + * @build jdk.test.lib.Utils + * jdk.test.lib.JDKToolFinder + * jdk.test.lib.process.ProcessTools + * @modules jdk.localedata + * @run junit LocaleTest */ public class LocaleTest { - private static Calendar TODAY = new GregorianCalendar(2024, Calendar.APRIL, 22); - private static String FORMAT = "%1$tY-%1$tB-%1$te %1$tA"; + private static final Calendar TODAY = new GregorianCalendar(2024, Calendar.APRIL, 22); + private static final String FORMAT = "%1$tY-%1$tB-%1$te %1$tA"; // We want to limit the expected strings within US-ASCII charset, as // the native encoding is determined as such, which is used by // the `Process` class under jtreg environment. - private static List EXPECTED = List.of( + private static final List EXPECTED = List.of( String.format(Locale.UK, FORMAT, TODAY), String.format(Locale.FRANCE, FORMAT, TODAY), String.format(Locale.GERMANY, FORMAT, TODAY), @@ -53,56 +65,61 @@ public class LocaleTest { String.format((Locale)null, FORMAT, TODAY) ); - public static void main(String... args) throws Throwable { - if (args.length == 0) { - // no arg will launch the child process that actually perform tests - var pb = ProcessTools.createTestJavaProcessBuilder( - "-Djdk.console=jdk.internal.le", - "LocaleTest", "dummy"); - var input = new File(System.getProperty("test.src", "."), "input.txt"); - pb.redirectInput(input); - var oa = ProcessTools.executeProcess(pb); - if (oa.getExitValue() == -1) { - System.out.println("System.console() returns null. Ignoring the test."); - } else { - var output = oa.asLines(); - var resultText = - """ - Actual output: %s - Expected output: %s - """.formatted(output, EXPECTED); - if (!output.equals(EXPECTED)) { - throw new RuntimeException("Standard out had unexpected strings:\n" + resultText); - } else { - oa.shouldHaveExitValue(0); - System.out.println("Formatting with explicit Locale succeeded.\n" + resultText); - } - } - } else { - var con = System.console(); - if (con != null) { - // tests these additional methods that take a Locale - con.format(Locale.UK, FORMAT, TODAY); - con.printf("\n"); - con.printf(Locale.FRANCE, FORMAT, TODAY); - con.printf("\n"); - con.readLine(Locale.GERMANY, FORMAT, TODAY); - con.printf("\n"); - con.readPassword(Locale.of("es"), FORMAT, TODAY); - con.printf("\n"); + @Test + void testLocale() throws Exception { + // check "expect" command availability + var expect = Paths.get("/usr/bin/expect"); + Assumptions.assumeTrue(Files.exists(expect) && Files.isExecutable(expect), + "'" + expect + "' not found. Test ignored."); - // tests null locale - con.format((Locale)null, FORMAT, TODAY); - con.printf("\n"); - con.printf((Locale)null, FORMAT, TODAY); - con.printf("\n"); - con.readLine((Locale)null, FORMAT, TODAY); - con.printf("\n"); - con.readPassword((Locale)null, FORMAT, TODAY); - } else { - // Exit with -1 - System.exit(-1); - } + // invoking "expect" command + OutputAnalyzer oa = ProcessTools.executeProcess( + "expect", + "-n", + TEST_SRC + "/locale.exp", + TEST_CLASSES, + TEST_JDK + "/bin/java", + getClass().getName()); + + var stdout = + oa.stdoutAsLines().stream().filter(Predicate.not(String::isEmpty)).toList(); + var resultText = + """ + Actual output: %s + Expected output: %s + """.formatted(stdout, EXPECTED); + if (!stdout.equals(EXPECTED)) { + throw new RuntimeException("Standard out had unexpected strings:\n" + resultText); + } else { + oa.shouldHaveExitValue(0); + System.out.println("Formatting with explicit Locale succeeded.\n" + resultText); + } + } + + public static void main(String... args) throws Throwable { + var con = System.console(); + if (con != null) { + // tests these additional methods that take a Locale + con.format(Locale.UK, FORMAT, TODAY); + con.printf("\n"); + con.printf(Locale.FRANCE, FORMAT, TODAY); + con.printf("\n"); + con.readLine(Locale.GERMANY, FORMAT, TODAY); + con.printf("\n"); + con.readPassword(Locale.of("es"), FORMAT, TODAY); + con.printf("\n"); + + // tests null locale + con.format((Locale)null, FORMAT, TODAY); + con.printf("\n"); + con.printf((Locale)null, FORMAT, TODAY); + con.printf("\n"); + con.readLine((Locale)null, FORMAT, TODAY); + con.printf("\n"); + con.readPassword((Locale)null, FORMAT, TODAY); + } else { + // Exit with -1 + System.exit(-1); } } } diff --git a/test/jdk/java/io/Console/ModuleSelectionTest.java b/test/jdk/java/io/Console/ModuleSelectionTest.java index d9885699ebf..332acf83fbd 100644 --- a/test/jdk/java/io/Console/ModuleSelectionTest.java +++ b/test/jdk/java/io/Console/ModuleSelectionTest.java @@ -23,21 +23,71 @@ /** * @test - * @bug 8295803 8299689 8351435 + * @bug 8295803 8299689 8351435 8361613 * @summary Tests System.console() returns correct Console (or null) from the expected * module. - * @modules java.base/java.io:+open - * @run main/othervm ModuleSelectionTest java.base - * @run main/othervm -Djdk.console=jdk.internal.le ModuleSelectionTest jdk.internal.le - * @run main/othervm -Djdk.console=java.base ModuleSelectionTest java.base - * @run main/othervm --limit-modules java.base ModuleSelectionTest java.base + * @library /test/lib + * @build jdk.test.lib.Utils + * jdk.test.lib.process.ProcessTools + * @run junit ModuleSelectionTest */ import java.io.Console; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.stream.Stream; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static jdk.test.lib.Utils.*; public class ModuleSelectionTest { + private static Stream options() { + return Stream.of( + Arguments.of("-Djdk.console=foo", "java.base"), + Arguments.of("-Djdk.console=java.base", "java.base"), + Arguments.of("-Djdk.console=jdk.internal.le", "jdk.internal.le"), + Arguments.of("--limit-modules java.base", "java.base") + ); + } + + @ParameterizedTest + @MethodSource("options") + void testNonTTY(String opts) throws Exception { + opts = opts + + " --add-opens java.base/java.io=ALL-UNNAMED ModuleSelectionTest null"; + OutputAnalyzer output = ProcessTools.executeTestJava(opts.split(" ")); + output.reportDiagnosticSummary(); + output.shouldHaveExitValue(0); + } + + @ParameterizedTest + @MethodSource("options") + void testTTY(String opts, String expected) throws Exception { + // check "expect" command availability + var expect = Paths.get("/usr/bin/expect"); + Assumptions.assumeTrue(Files.exists(expect) && Files.isExecutable(expect), + "'" + expect + "' not found. Test ignored."); + + opts = "expect -n " + TEST_SRC + "/moduleSelection.exp " + + TEST_CLASSES + " " + + expected + " " + + TEST_JDK + "/bin/java" + + " --add-opens java.base/java.io=ALL-UNNAMED " + + opts; + // invoking "expect" command + OutputAnalyzer output = ProcessTools.executeProcess(opts.split(" ")); + output.reportDiagnosticSummary(); + output.shouldHaveExitValue(0); + } + public static void main(String... args) throws Throwable { var con = System.console(); var pc = Class.forName("java.io.ProxyingConsole"); @@ -49,10 +99,7 @@ public class ModuleSelectionTest { .findGetter(pc, "delegate", jdkc) .invoke(con) : null; - var expected = switch (args[0]) { - case "java.base" -> istty ? "java.base" : "null"; - default -> args[0]; - }; + var expected = args[0]; var actual = con == null ? "null" : impl.getClass().getModule().getName(); if (!actual.equals(expected)) { @@ -62,7 +109,7 @@ public class ModuleSelectionTest { Actual: %s """.formatted(expected, actual)); } else { - System.out.printf("%s is the expected implementation. (tty: %s)\n", impl, istty); + System.out.printf("%s is the expected implementation. (tty: %s)\n", actual, istty); } } } diff --git a/test/jdk/java/io/Console/defaultCharset.exp b/test/jdk/java/io/Console/defaultCharset.exp new file mode 100644 index 00000000000..5b1418db28c --- /dev/null +++ b/test/jdk/java/io/Console/defaultCharset.exp @@ -0,0 +1,32 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# simply invoking java under expect command +set classpath [lrange $argv 0 0] +set java [lrange $argv 1 1] +set stdoutProp [lrange $argv 2 2] +set clsname [lrange $argv 3 3] +eval spawn $java -classpath $classpath $stdoutProp $clsname +expect eof +set result [wait] +exit [lindex $result 3] diff --git a/test/jdk/java/io/Console/locale.exp b/test/jdk/java/io/Console/locale.exp new file mode 100644 index 00000000000..a88ea43feac --- /dev/null +++ b/test/jdk/java/io/Console/locale.exp @@ -0,0 +1,37 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# simply invoking java under expect command +set classpath [lrange $argv 0 0] +set java [lrange $argv 1 1] +set clsname [lrange $argv 2 2] +eval spawn -noecho $java -classpath $classpath $clsname + +# sends CR 4 times (readLine x 2, readPassword x 2) +send "\r" +send "\r" +send "\r" +send "\r" +expect eof +set result [wait] +exit [lindex $result 3] diff --git a/test/jdk/java/io/Console/moduleSelection.exp b/test/jdk/java/io/Console/moduleSelection.exp new file mode 100644 index 00000000000..2b44afe72e4 --- /dev/null +++ b/test/jdk/java/io/Console/moduleSelection.exp @@ -0,0 +1,30 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# simply invoking java under expect command +set classpath [lrange $argv 0 0] +set expected [lrange $argv 1 1] +set java [lrange $argv 2 2] +set opts [lrange $argv 3 end] +eval spawn $java $opts -classpath $classpath ModuleSelectionTest $expected +expect eof diff --git a/test/jdk/java/lang/IO/IO.java b/test/jdk/java/lang/IO/IO.java index dbb83db5f80..2b13657b58e 100644 --- a/test/jdk/java/lang/IO/IO.java +++ b/test/jdk/java/lang/IO/IO.java @@ -50,10 +50,9 @@ import static org.junit.jupiter.api.Assertions.*; /* * @test - * @bug 8305457 8342936 8351435 8344706 + * @bug 8305457 8342936 8351435 8344706 8361613 * @summary java.lang.IO tests * @library /test/lib - * @modules jdk.internal.le * @run junit IO */ @ExtendWith(IO.TimingExtension.class) @@ -78,22 +77,6 @@ public class IO { } catch (Exception _) { } } - /* - * Unlike printTest, which tests a _default_ console that is normally - * jdk.internal.org.jline.JdkConsoleProviderImpl, this test tests - * jdk.internal.io.JdkConsoleImpl. Those console implementations operate - * in different conditions and, thus, are tested separately. - * - * To test jdk.internal.io.JdkConsoleImpl one needs to ensure that both - * conditions are met: - * - * - a non-existent console provider is requested - * - isatty is true - * - * To achieve isatty, the test currently uses the EXPECT(1) Unix command, - * which does not work for Windows. Later, a library like pty4j or JPty - * might be used instead of EXPECT, to cover both Unix and Windows. - */ @ParameterizedTest @ValueSource(strings = {"println", "print"}) public void outputTestInteractive(String mode) throws Exception { @@ -102,8 +85,6 @@ public class IO { expect.toString(), Path.of(testSrc, "output.exp").toAbsolutePath().toString(), System.getProperty("test.jdk") + "/bin/java", - "--enable-preview", - "-Djdk.console=gibberish", Path.of(testSrc, "Output.java").toAbsolutePath().toString(), mode); assertEquals(0, output.getExitValue()); @@ -130,7 +111,7 @@ public class IO { */ @ParameterizedTest @MethodSource("args") - public void inputTestInteractive(String console, String prompt) throws Exception { + public void inputTestInteractive(String prompt) throws Exception { var testSrc = System.getProperty("test.src", "."); var command = new ArrayList(); command.add(expect.toString()); @@ -138,9 +119,6 @@ public class IO { : "input"; command.add(Path.of(testSrc, expectInputName + ".exp").toAbsolutePath().toString()); command.add(System.getProperty("test.jdk") + "/bin/java"); - command.add("--enable-preview"); - if (console != null) - command.add("-Djdk.console=" + console); command.add(Path.of(testSrc, "Input.java").toAbsolutePath().toString()); command.add(prompt == null ? "0" : PROMPT_NONE.equals(prompt) ? "2" : "1"); command.add(String.valueOf(prompt)); @@ -152,33 +130,11 @@ public class IO { private static final String PROMPT_NONE = "prompt-none"; public static Stream args() { - // cross product: consoles x prompts - return Stream.of("jdk.internal.le", "gibberish").flatMap(console -> Stream.of(null, "?", "%s", PROMPT_NONE) - .map(prompt -> new String[]{console, prompt}).map(Arguments::of)); + // prompts + return Stream.of(null, "?", "%s", PROMPT_NONE).map(Arguments::of); } } - @ParameterizedTest - @ValueSource(strings = {"println", "print"}) - public void printTest(String mode) throws Exception { - var file = Path.of(System.getProperty("test.src", "."), "Output.java") - .toAbsolutePath().toString(); - var pb = ProcessTools.createTestJavaProcessBuilder("-Djdk.console=jdk.internal.le", "--enable-preview", file, mode); - OutputAnalyzer output = ProcessTools.executeProcess(pb); - assertEquals(0, output.getExitValue()); - assertTrue(output.getStderr().isEmpty()); - output.reportDiagnosticSummary(); - String out = output.getStdout(); - // The first half of the output is produced by Console, the second - // half is produced by IO: those halves must match. - // Executing Console and IO in the same VM (as opposed to - // consecutive VM runs, which are cleaner) to be able to compare string - // representation of objects. - assertFalse(out.isBlank()); - assertEquals(out.substring(0, out.length() / 2), - out.substring(out.length() / 2)); - } - @Test //JDK-8342936 public void printlnNoParamsTest() throws Exception { var file = Path.of("PrintlnNoParams.java"); @@ -193,7 +149,7 @@ public class IO { } """); } - var pb = ProcessTools.createTestJavaProcessBuilder("-Djdk.console=jdk.internal.le", "--enable-preview", file.toString()); + var pb = ProcessTools.createTestJavaProcessBuilder(file.toString()); OutputAnalyzer output = ProcessTools.executeProcess(pb); assertEquals(0, output.getExitValue()); assertTrue(output.getStderr().isEmpty()); diff --git a/test/jdk/jdk/internal/jline/JLineConsoleProviderTest.java b/test/jdk/jdk/internal/jline/JLineConsoleProviderTest.java index 71590040685..445da167c5f 100644 --- a/test/jdk/jdk/internal/jline/JLineConsoleProviderTest.java +++ b/test/jdk/jdk/internal/jline/JLineConsoleProviderTest.java @@ -23,16 +23,19 @@ /** * @test - * @bug 8331535 8351435 8347050 + * @bug 8331535 8351435 8347050 8361613 * @summary Verify the jdk.internal.le's console provider works properly. - * @modules jdk.internal.le + * @modules java.base/jdk.internal.io + * jdk.internal.le/jdk.internal.org.jline * @library /test/lib - * @run main/othervm -Djdk.console=jdk.internal.le JLineConsoleProviderTest + * @run main JLineConsoleProviderTest */ import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.Objects; +import jdk.internal.org.jline.JdkConsoleProviderImpl; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; @@ -66,8 +69,13 @@ public class JLineConsoleProviderTest { String input, String expectedOut) throws Exception { ProcessBuilder builder = - ProcessTools.createTestJavaProcessBuilder("-Djdk.console=jdk.internal.le", ConsoleTest.class.getName(), - testName); + ProcessTools.createTestJavaProcessBuilder( + "--add-exports", + "java.base/jdk.internal.io=ALL-UNNAMED", + "--add-exports", + "jdk.internal.le/jdk.internal.org.jline=ALL-UNNAMED", + ConsoleTest.class.getName(), + testName); OutputAnalyzer output = ProcessTools.executeProcess(builder, input); output.waitFor(); @@ -98,16 +106,18 @@ public class JLineConsoleProviderTest { public static class ConsoleTest { public static void main(String... args) { + // directly instantiate JLine JdkConsole, simulating isTTY=true + var impl = new JdkConsoleProviderImpl().console(true, StandardCharsets.UTF_8, StandardCharsets.UTF_8); switch (args[0]) { case "testCorrectOutputReadLine" -> - System.console().readLine("%%s"); + impl.readLine(null, "%%s"); case "testCorrectOutputReadPassword" -> - System.console().readPassword("%%s"); + impl.readPassword(null, "%%s"); case "readAndPrint" -> - System.out.println("'" + System.console().readLine() + "'"); + System.out.println("'" + impl.readLine() + "'"); case "readAndPrint2" -> { - System.out.println("1: '" +System.console().readLine() + "'"); - System.out.println("2: '" + System.console().readLine() + "'"); + System.out.println("1: '" + impl.readLine() + "'"); + System.out.println("2: '" + impl.readLine() + "'"); } default -> throw new UnsupportedOperationException(args[0]); } diff --git a/test/jdk/jdk/internal/jline/LazyJdkConsoleProvider.java b/test/jdk/jdk/internal/jline/LazyJdkConsoleProvider.java index acf0c848b43..a7533796b7c 100644 --- a/test/jdk/jdk/internal/jline/LazyJdkConsoleProvider.java +++ b/test/jdk/jdk/internal/jline/LazyJdkConsoleProvider.java @@ -23,15 +23,19 @@ /** * @test - * @bug 8333086 8344706 + * @bug 8333086 8344706 8361613 * @summary Verify the JLine backend is not initialized for simple printing. - * @enablePreview - * @modules jdk.internal.le/jdk.internal.org.jline.reader + * @modules java.base/jdk.internal.io + * jdk.internal.le/jdk.internal.org.jline + * jdk.internal.le/jdk.internal.org.jline.reader * jdk.internal.le/jdk.internal.org.jline.terminal * @library /test/lib * @run main LazyJdkConsoleProvider */ +import java.nio.charset.StandardCharsets; + +import jdk.internal.org.jline.JdkConsoleProviderImpl; import jdk.internal.org.jline.reader.LineReader; import jdk.internal.org.jline.terminal.Terminal; @@ -41,19 +45,18 @@ import jdk.test.lib.process.ProcessTools; public class LazyJdkConsoleProvider { public static void main(String... args) throws Throwable { + // directly instantiate JLine JdkConsole, simulating isTTY=true switch (args.length > 0 ? args[0] : "default") { case "write" -> { - System.console().printf("Hello!\n"); - System.console().printf("Hello!"); - System.console().format("\nHello!\n"); - System.console().flush(); - IO.println("Hello!"); - IO.print("Hello!"); - } - case "read" -> System.console().readLine("Hello!"); - case "IO-read" -> { - IO.readln("Hello!"); + var impl = new JdkConsoleProviderImpl().console(true, StandardCharsets.UTF_8, StandardCharsets.UTF_8); + impl.println("Hello!\n"); + impl.println("Hello!"); + impl.format(null, "\nHello!\n"); + impl.flush(); } + case "read" -> new JdkConsoleProviderImpl() + .console(true, StandardCharsets.UTF_8, StandardCharsets.UTF_8) + .readLine(null, "Hello!"); case "default" -> { new LazyJdkConsoleProvider().runTest(); } @@ -64,14 +67,15 @@ public class LazyJdkConsoleProvider { record TestCase(String testKey, String expected, String notExpected) {} TestCase[] testCases = new TestCase[] { new TestCase("write", null, Terminal.class.getName()), - new TestCase("read", LineReader.class.getName(), null), - new TestCase("IO-read", null, Terminal.class.getName()) + new TestCase("read", LineReader.class.getName(), null) }; for (TestCase tc : testCases) { ProcessBuilder builder = - ProcessTools.createTestJavaProcessBuilder("--enable-preview", - "-verbose:class", - "-Djdk.console=jdk.internal.le", + ProcessTools.createTestJavaProcessBuilder("-verbose:class", + "--add-exports", + "java.base/jdk.internal.io=ALL-UNNAMED", + "--add-exports", + "jdk.internal.le/jdk.internal.org.jline=ALL-UNNAMED", LazyJdkConsoleProvider.class.getName(), tc.testKey()); OutputAnalyzer output = ProcessTools.executeProcess(builder, ""); diff --git a/test/jdk/jdk/internal/jline/RedirectedStdOut.java b/test/jdk/jdk/internal/jline/RedirectedStdOut.java deleted file mode 100644 index 71419f96c73..00000000000 --- a/test/jdk/jdk/internal/jline/RedirectedStdOut.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @bug 8330998 8351435 - * @summary Verify that even if the stdout is redirected java.io.Console will - * use it for writing. - * @modules jdk.internal.le - * @library /test/lib - * @run main RedirectedStdOut runRedirectAllTest - * @run main/othervm --enable-native-access=ALL-UNNAMED RedirectedStdOut runRedirectOutOnly - */ - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.lang.foreign.Arena; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.Linker; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SymbolLookup; -import java.lang.foreign.ValueLayout; -import java.lang.invoke.MethodHandle; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Objects; -import java.util.Optional; - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; - -public class RedirectedStdOut { - private static final String OUTPUT = "Hello!"; - - public static void main(String... args) throws Throwable { - RedirectedStdOut.class.getDeclaredMethod(args[0]) - .invoke(new RedirectedStdOut()); - } - - //verify the case where neither stdin/out/err is attached to a terminal, - //this test is weaker, but more reliable: - void runRedirectAllTest() throws Exception { - ProcessBuilder builder = - ProcessTools.createTestJavaProcessBuilder("-Djdk.console=jdk.internal.le", ConsoleTest.class.getName()); - OutputAnalyzer output = ProcessTools.executeProcess(builder); - - output.waitFor(); - - if (output.getExitValue() != 0) { - throw new AssertionError("Unexpected return value: " + output.getExitValue() + - ", actualOut: " + output.getStdout() + - ", actualErr: " + output.getStderr()); - } - - String expectedOut = OUTPUT; - String actualOut = output.getStdout(); - - if (!Objects.equals(expectedOut, actualOut)) { - throw new AssertionError("Unexpected stdout content. " + - "Expected: '" + expectedOut + "'" + - ", got: '" + actualOut + "'"); - } - - String expectedErr = ""; - String actualErr = output.getStderr(); - - if (!Objects.equals(expectedErr, actualErr)) { - throw new AssertionError("Unexpected stderr content. " + - "Expected: '" + expectedErr + "'" + - ", got: '" + actualErr + "'"); - } - } - - //verify the case where stdin is attached to a terminal, - //this test allocates pty, and it might be skipped, if the appropriate - //native functions cannot be found - //it also leaves the VM in a broken state (with a pty attached), and so - //should run in a separate VM instance - void runRedirectOutOnly() throws Throwable { - Path stdout = Path.of(".", "stdout.txt").toAbsolutePath(); - - Files.deleteIfExists(stdout); - - Linker linker = Linker.nativeLinker(); - SymbolLookup stdlib = linker.defaultLookup(); - MemorySegment parent = Arena.global().allocate(ValueLayout.ADDRESS); - MemorySegment child = Arena.global().allocate(ValueLayout.ADDRESS); - Optional openptyAddress = stdlib.find("openpty"); - - if (openptyAddress.isEmpty()) { - System.out.println("Cannot lookup openpty."); - //does not have forkpty, ignore - return ; - } - - Optional loginttyAddress = stdlib.find("login_tty"); - - if (loginttyAddress.isEmpty()) { - System.out.println("Cannot lookup login_tty."); - //does not have forkpty, ignore - return ; - } - - FunctionDescriptor openttyDescriptor = - FunctionDescriptor.of(ValueLayout.JAVA_INT, - ValueLayout.ADDRESS, - ValueLayout.ADDRESS, - ValueLayout.ADDRESS, - ValueLayout.ADDRESS, - ValueLayout.ADDRESS); - MethodHandle forkpty = linker.downcallHandle(openptyAddress.get(), - openttyDescriptor); - int res = (int) forkpty.invoke(parent, - child, - MemorySegment.NULL, - MemorySegment.NULL, - MemorySegment.NULL); - - if (res != 0) { - throw new AssertionError(); - } - - //set the current VM's in/out to the terminal: - FunctionDescriptor loginttyDescriptor = - FunctionDescriptor.of(ValueLayout.JAVA_INT, - ValueLayout.JAVA_INT); - MethodHandle logintty = linker.downcallHandle(loginttyAddress.get(), - loginttyDescriptor); - logintty.invoke(child.get(ValueLayout.JAVA_INT, 0)); - - //createTestJavaProcessBuilder logs to (current process') System.out, but - //that may not work since the redirect. Setting System.out to a scratch value: - System.setOut(new PrintStream(new ByteArrayOutputStream())); - - ProcessBuilder builder = - ProcessTools.createTestJavaProcessBuilder("-Djdk.console=jdk.internal.le", ConsoleTest.class.getName()); - - builder.inheritIO(); - builder.redirectOutput(stdout.toFile()); - - OutputAnalyzer output = ProcessTools.executeProcess(builder); - - output.waitFor(); - - String expectedOut = OUTPUT; - String actualOut = Files.readString(stdout); - - if (!Objects.equals(expectedOut, actualOut)) { - throw new AssertionError("Unexpected stdout content. " + - "Expected: '" + expectedOut + "'" + - ", got: '" + actualOut + "'"); - } - } - - public static class ConsoleTest { - public static void main(String... args) { - System.console().printf(OUTPUT); - System.exit(0); - } - } -}