Path: blob/master/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java
66644 views
/*1* Copyright (c) 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*/2223import static org.testng.Assert.*;2425import org.testng.Assert;26import org.testng.annotations.DataProvider;27import org.testng.annotations.Test;2829import jdk.test.lib.process.ProcessTools;3031import jdk.test.lib.hexdump.HexPrinter;32import jdk.test.lib.hexdump.HexPrinter.Formatters;3334import java.io.BufferedReader;35import java.io.BufferedWriter;36import java.io.IOException;37import java.io.Writer;38import java.nio.ByteBuffer;39import java.nio.file.Path;40import java.nio.file.Files;41import java.nio.charset.Charset;42import java.nio.charset.StandardCharsets;43import java.nio.charset.UnsupportedCharsetException;44import java.util.List;45import java.util.Locale;4647import jtreg.SkippedException;4849/*50* @test51* @library /test/lib52* @build jdk.test.lib.process.ProcessTools jdk.test.lib.hexdump.HexPrinter53* @run testng ReaderWriterTest54*/5556@Test57public class ReaderWriterTest {5859static final String ASCII = "ASCII: \u0000_A-Z_a-Z_\u007C_\u007D_\u007E_\u007F_;";60static final String ISO_8859_1 = " Symbols: \u00AB_\u00BB_\u00fc_\u00fd_\u00fe_\u00ff;";61static final String FRACTIONS = " Fractions: \u00bc_\u00bd_\u00be_\u00bf;";6263public static final String TESTCHARS = "OneWay: " + ASCII + ISO_8859_1 + FRACTIONS;64public static final String ROUND_TRIP_TESTCHARS = "RoundTrip: " + ASCII + ISO_8859_1 + FRACTIONS;6566@DataProvider(name="CharsetCases")67static Object[][] charsetCases() {68return new Object[][] {69{"UTF-8"},70{"ISO8859-1"},71{"US-ASCII"},72};73}7475/**76* Test the defaults case of native.encoding. No extra command line flags or switches.77*/78@Test79void testCaseNativeEncoding() throws IOException {80String nativeEncoding = System.getProperty("native.encoding");81Charset cs = Charset.forName(nativeEncoding);82System.out.println("Native.encoding Charset: " + cs);8384ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("ReaderWriterTest$ChildWithCharset");85Process p = pb.start();86writeTestChars(p.outputWriter());87checkReader(p.inputReader(), cs, "Out");88checkReader(p.errorReader(), cs, "Err");89try {90int exitValue = p.waitFor();91if (exitValue != 0)92System.out.println("exitValue: " + exitValue);93} catch (InterruptedException ie) {94Assert.fail("waitFor interrupted");95}96}9798/**99* Test that redirects of input and error streams result in Readers that are empty.100* Test that when the output to a process is redirected, the writer acts as101* a null stream and throws an exception as expected for a null output stream102* as specified by ProcessBuilder.103*/104@Test105void testRedirects() throws IOException {106String nativeEncoding = System.getProperty("native.encoding");107Charset cs = Charset.forName(nativeEncoding);108System.out.println("Native.encoding Charset: " + cs);109110Path inPath = Path.of("InFile.tmp");111BufferedWriter inWriter = Files.newBufferedWriter(inPath);112inWriter.close();113114Path outPath = Path.of("OutFile.tmp");115Path errorPath = Path.of("ErrFile.tmp");116117for (int errType = 1; errType < 4; errType++) {118// Three cases to test for which the error stream is empty119// 1: redirectErrorStream(false); redirect of errorOutput to a file120// 2: redirectErrorStream(true); no redirect of errorOutput121// 3: redirectErrorStream(true); redirect of errorOutput to a file122123ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("ReaderWriterTest$ChildWithCharset");124pb.redirectInput(inPath.toFile());125pb.redirectOutput(outPath.toFile());126if (errType == 1 || errType == 3) {127pb.redirectError(errorPath.toFile());128}129if (errType == 2 || errType == 3) {130pb.redirectErrorStream(true);131}132Process p = pb.start();133// Output has been redirected to a null stream; success is IOException on the write134try {135BufferedWriter wr = p.outputWriter();136wr.write("X");137wr.flush();138Assert.fail("writing to null stream should throw IOException");139} catch (IOException ioe) {140// Normal, A Null output stream is closed when created.141}142143// InputReader should be empty; and at EOF144BufferedReader inputReader = p.inputReader();145int ch = inputReader.read();146Assert.assertEquals(ch, -1, "inputReader not at EOF: ch: " + (char)ch);147148// InputReader should be empty; and at EOF149BufferedReader errorReader = p.errorReader();150ch = errorReader.read();151Assert.assertEquals(ch, -1, "errorReader not at EOF: ch: " + (char)ch);152153try {154int exitValue = p.waitFor();155if (exitValue != 0) System.out.println("exitValue: " + exitValue);156} catch (InterruptedException ie) {157Assert.fail("waitFor interrupted");158}159}160}161162/**163* Write the test characters to the child using the Process.outputWriter.164* @param writer the Writer165* @throws IOException if an I/O error occurs166*/167private static void writeTestChars(Writer writer) throws IOException {168// Write the test data to the child169try (writer) {170writer.append(ROUND_TRIP_TESTCHARS);171writer.append(System.lineSeparator());172}173}174175/**176* Test a child with a character set.177* A Process is spawned; characters are written to and read from the child178* using the character set and compared.179*180* @param encoding a charset name181*/182@Test(dataProvider = "CharsetCases", enabled = true)183void testCase(String encoding) throws IOException {184Charset cs = null;185try {186cs = Charset.forName(encoding);187System.out.println("Charset: " + cs);188} catch (UnsupportedCharsetException use) {189throw new SkippedException("Charset not supported: " + encoding);190}191String cleanCSName = cleanCharsetName(cs);192193ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(194"-Dsun.stdout.encoding=" + cleanCSName, // Encode in the child using the charset195"-Dsun.stderr.encoding=" + cleanCSName,196"ReaderWriterTest$ChildWithCharset");197198Process p = pb.start();199// Write the test data to the child200writeTestChars(p.outputWriter(cs));201checkReader(p.inputReader(cs), cs, "Out");202checkReader(p.errorReader(cs), cs, "Err");203try {204int exitValue = p.waitFor();205if (exitValue != 0)206System.out.println("exitValue: " + exitValue);207} catch (InterruptedException ie) {208209}210}211212/**213* Test passing null when a charset is expected214* @throws IOException if an I/O error occurs; not expected215*/216@Test217void testNullCharsets() throws IOException {218// Launch a child; its behavior is not interesting and is ignored219ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(220"ReaderWriterTest$ChildWithCharset");221222Process p = pb.start();223try {224writeTestChars(p.outputWriter(null));225Assert.fail("Process.outputWriter(null) did not throw NPE");226} catch (NullPointerException npe) {227// expected, ignore228}229try {230checkReader(p.inputReader(null), null, "Out");231Assert.fail("Process.inputReader(null) did not throw NPE");232} catch (NullPointerException npe) {233// expected, ignore234}235try {236checkReader(p.errorReader(null), null, "Err");237Assert.fail("Process.errorReader(null) did not throw NPE");238} catch (NullPointerException npe) {239// expected, ignore240}241242p.destroyForcibly();243try {244// Collect the exit status to cleanup after the process; but ignore it245p.waitFor();246} catch (InterruptedException ie) {247// Ignored248}249}250251/**252* Test passing different charset on multiple calls when the same charset is expected.253* @throws IOException if an I/O error occurs; not expected254*/255@Test256void testIllegalArgCharsets() throws IOException {257String nativeEncoding = System.getProperty("native.encoding");258Charset cs = Charset.forName(nativeEncoding);259System.out.println("Native.encoding Charset: " + cs);260Charset otherCharset = cs.equals(StandardCharsets.UTF_8)261? StandardCharsets.ISO_8859_1262: StandardCharsets.UTF_8;263264// Launch a child; its behavior is not interesting and is ignored265ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(266"ReaderWriterTest$ChildWithCharset");267268Process p = pb.start();269try {270var writer = p.outputWriter(cs);271writer = p.outputWriter(cs); // try again with same272writer = p.outputWriter(otherCharset); // this should throw273Assert.fail("Process.outputWriter(otherCharset) did not throw IllegalStateException");274} catch (IllegalStateException ile) {275// expected, ignore276System.out.println(ile);277}278try {279var reader = p.inputReader(cs);280reader = p.inputReader(cs); // try again with same281reader = p.inputReader(otherCharset); // this should throw282Assert.fail("Process.inputReader(otherCharset) did not throw IllegalStateException");283} catch (IllegalStateException ile) {284// expected, ignore285System.out.println(ile);286}287try {288var reader = p.errorReader(cs);289reader = p.errorReader(cs); // try again with same290reader = p.errorReader(otherCharset); // this should throw291Assert.fail("Process.errorReader(otherCharset) did not throw IllegalStateException");292} catch (IllegalStateException ile) {293// expected, ignore294System.out.println(ile);295}296297p.destroyForcibly();298try {299// Collect the exit status to cleanup after the process; but ignore it300p.waitFor();301} catch (InterruptedException ie) {302// Ignored303}304}305306private static void checkReader(BufferedReader reader, Charset cs, String label) throws IOException {307try (BufferedReader in = reader) {308String prefix = " " + label + ": ";309String firstline = in.readLine();310System.out.append(prefix).println(firstline);311String secondline = in.readLine();312System.out.append(prefix).println(secondline);313for (String line = in.readLine(); line != null; line = in.readLine()) {314System.out.append(prefix).append(line);315System.out.println();316}317ByteBuffer bb = cs.encode(TESTCHARS);318String reencoded = cs.decode(bb).toString();319if (!firstline.equals(reencoded))320diffStrings(firstline, reencoded);321assertEquals(firstline, reencoded, label + " Test Chars");322323bb = cs.encode(ROUND_TRIP_TESTCHARS);324reencoded = cs.decode(bb).toString();325if (!secondline.equals(reencoded))326diffStrings(secondline, reencoded);327assertEquals(secondline, reencoded, label + " Round Trip Test Chars");328}329}330331/**332* A cleaned up Charset name that is suitable for Linux LANG environment variable.333* If there are two '-'s the first one is removed.334* @param cs a Charset335* @return the cleanedup Charset name336*/337private static String cleanCharsetName(Charset cs) {338String name = cs.name();339int ndx = name.indexOf('-');340if (ndx >= 0 && name.indexOf('-', ndx + 1) >= 0) {341name = name.substring(0, ndx) + name.substring(ndx + 1);342}343return name;344}345346private static void diffStrings(String actual, String expected) {347if (actual.equals(expected))348return;349int lenDiff = expected.length() - actual.length();350if (lenDiff != 0)351System.out.println("String lengths: " + actual.length() + " != " + expected.length());352int first; // find first mismatched character353for (first = 0; first < Math.min(actual.length(), expected.length()); first++) {354if (actual.charAt(first) != expected.charAt(first))355break;356}357int last;358for (last = actual.length() - 1; last >= 0 && (last + lenDiff) >= 0; last--) {359if (actual.charAt(last) != expected.charAt(last + lenDiff))360break; // last mismatched character361}362System.out.printf("actual vs expected[%3d]: 0x%04x != 0x%04x%n", first, (int)actual.charAt(first), (int)expected.charAt(first));363System.out.printf("actual vs expected[%3d]: 0x%04x != 0x%04x%n", last, (int)actual.charAt(last), (int)expected.charAt(last));364System.out.printf("actual [%3d-%3d]: %s%n", first, last, actual.substring(first, last+1));365System.out.printf("expected[%3d-%3d]: %s%n", first, last, expected.substring(first, last + lenDiff + 1));366}367368static class ChildWithCharset {369public static void main(String[] args) {370String nativeEncoding = System.getProperty("native.encoding");371System.out.println(TESTCHARS);372byte[] bytes = null;373try {374bytes = System.in.readAllBytes();375System.out.write(bytes); // echo bytes back to parent on stdout376} catch (IOException ioe) {377ioe.printStackTrace(); // Seen by the parent378}379System.out.println("native.encoding: " + nativeEncoding);380System.out.println("sun.stdout.encoding: " + System.getProperty("sun.stdout.encoding"));381System.out.println("LANG: " + System.getenv().get("LANG"));382383System.err.println(TESTCHARS);384try {385System.err.write(bytes); // echo bytes back to parent on stderr386} catch (IOException ioe) {387ioe.printStackTrace(); // Seen by the parent388}389System.err.println("native.encoding: " + nativeEncoding);390System.err.println("sun.stderr.encoding: " + System.getProperty("sun.stderr.encoding"));391}392}393}394395396