1 /*
   2  * Copyright (c) 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 import java.io.ByteArrayOutputStream;
  25 import java.io.FileInputStream;
  26 import java.io.FileOutputStream;
  27 import java.io.IOException;
  28 import java.io.Reader;
  29 import java.io.Writer;
  30 import java.nio.channels.Channels;
  31 import java.nio.channels.ReadableByteChannel;
  32 import java.nio.channels.WritableByteChannel;
  33 import java.nio.charset.Charset;
  34 import java.nio.charset.MalformedInputException;
  35 import java.nio.charset.StandardCharsets;
  36 import java.nio.file.Paths;
  37 import org.testng.Assert;
  38 import org.testng.annotations.DataProvider;
  39 import org.testng.annotations.Test;
  40 
  41 /**
  42  * @test
  43  * @bug 8183743
  44  * @summary Test to verify the new overload method with Charset functions the same
  45  * as the existing method that takes a charset name.
  46  * @run testng EncodingTest
  47  */
  48 public class EncodingTest {
  49     static final int ITERATIONS = 2;
  50     public static final String CS_UTF8 = StandardCharsets.UTF_8.name();
  51     public static final String CS_ISO8859 = StandardCharsets.ISO_8859_1.name();
  52     static String USER_DIR = System.getProperty("user.dir", ".");
  53 
  54     // malformed input: a high surrogate without the low surrogate
  55     static char[] illChars = {
  56         '\u00fa', '\ud800'
  57     };
  58 
  59     static byte[] data = getData();
  60 
  61     static byte[] getData() {
  62         try {
  63             String str1 = "A string that contains ";
  64             String str2 = " , an invalid character for UTF-8.";
  65 
  66             ByteArrayOutputStream baos = new ByteArrayOutputStream();
  67             baos.write(str1.getBytes());
  68             baos.write(0xFA);
  69             baos.write(str2.getBytes());
  70             return baos.toByteArray();
  71         } catch (IOException ex) {
  72             return null; //shouldn't happen
  73         }
  74     }
  75 
  76     String testFile = Paths.get(USER_DIR, "channelsEncodingTest.txt").toString();
  77     String testIllegalInput = Paths.get(USER_DIR, "channelsIllegalInputTest.txt").toString();
  78     String testIllegalOutput = Paths.get(USER_DIR, "channelsIllegalOutputTest.txt").toString();
  79 
  80 
  81     /*
  82      * DataProvider for read and write test.
  83      * Writes and reads with the same encoding
  84      */
  85     @DataProvider(name = "writeAndRead")
  86     public Object[][] getWRParameters() {
  87         return new Object[][]{
  88             {testFile, StandardCharsets.ISO_8859_1.name(), null,
  89                 StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1},
  90             {testFile, null, StandardCharsets.ISO_8859_1,
  91                 StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1},
  92             {testFile, StandardCharsets.UTF_8.name(), null,
  93                 StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8},
  94             {testFile, null, StandardCharsets.UTF_8,
  95                 StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8}
  96         };
  97     }
  98 
  99     /*
 100      * DataProvider for illegal input test
 101      * Writes the data in ISO8859 and reads with UTF8, expects MalformedInputException
 102      */
 103     @DataProvider(name = "illegalInput")
 104     public Object[][] getParameters() {
 105         return new Object[][]{
 106             {testIllegalInput, StandardCharsets.ISO_8859_1.name(), null, StandardCharsets.UTF_8.name(), null},
 107             {testIllegalInput, StandardCharsets.ISO_8859_1.name(), null, null, StandardCharsets.UTF_8},
 108             {testIllegalInput, null, StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8.name(), null},
 109             {testIllegalInput, null, StandardCharsets.ISO_8859_1, null, StandardCharsets.UTF_8},
 110         };
 111     }
 112 
 113     /*
 114      * DataProvider for illegal output test
 115      * Attemps to write some malformed chars, expects MalformedInputException
 116      */
 117     @DataProvider(name = "illegalOutput")
 118     public Object[][] getWriteParameters() {
 119         return new Object[][]{
 120             {testIllegalOutput, StandardCharsets.UTF_8.name(), null},
 121             {testIllegalOutput, null, StandardCharsets.UTF_8}
 122         };
 123     }
 124 
 125     /**
 126      * Verifies that the Readers created with the following methods are
 127      * equivalent:
 128      * newReader(ReadableByteChannel ch, String csName)
 129      * newReader(ReadableByteChannel ch, Charset charset)
 130      *
 131      * The verification follows the following steps:
 132      * Writes a file with a writer created with the specified charset
 133      * Reads it with a reader created with newReader using the same charset;
 134      * Compares that the results are the same.
 135      *
 136      * @param file the file name
 137      * @param csnWriter the charset name for creating the writer
 138      * @param charsetWriter the charset for creating the writer
 139      * @param csnReader the charset name for creating the reader
 140      * @param charsetReader the charset for creating the reader
 141      * @throws Exception
 142      */
 143     @Test(dataProvider = "writeAndRead")
 144     public void testWriteAndRead(String file, String csnWriter, Charset charsetWriter,
 145             String csnReader, Charset charsetReader) throws Exception {
 146         writeToFile(data, file, csnWriter, charsetWriter);
 147         // read using charset name
 148         String result1 = readFileToString(file, csnReader, null);
 149         String result2 = readFileToString(file, null, charsetReader);
 150 
 151         Assert.assertEquals(result1, result2);
 152     }
 153 
 154     /**
 155      * Verifies that MalformedInputException is thrown when an input byte sequence
 156      * is illegal for given charset that is configured for the reader.
 157      *
 158      * @param file the file to be read
 159      * @param csnWriter the charset name for creating the writer
 160      * @param charsetWriter the charset for creating the writer
 161      * @param csnReader the charset name for creating the reader
 162      * @param charsetReader the charset for creating the reader
 163      * @throws Exception
 164      */
 165     @Test(dataProvider = "illegalInput", expectedExceptions = MalformedInputException.class)
 166     void testMalformedInput(String file, String csnWriter, Charset charsetWriter,
 167             String csnReader, Charset charsetReader) throws Exception {
 168         writeToFile(data, file, csnWriter, charsetWriter);
 169         readFileToString(file, csnReader, charsetReader);
 170     }
 171 
 172     /**
 173      * Attempts to write illegal characters using a writer created by calling
 174      * the newWriter method and expects a MalformedInputException.
 175      *
 176      * @param fileName the file name
 177      * @param csn the charset name
 178      * @param charset the charset
 179      * @throws Exception
 180      */
 181     @Test(dataProvider = "illegalOutput", expectedExceptions = MalformedInputException.class)
 182     public void testMalformedOutput(String fileName, String csn, Charset charset)
 183             throws Exception {
 184         try (FileOutputStream fos = new FileOutputStream(fileName);
 185                 WritableByteChannel wbc = (WritableByteChannel) fos.getChannel();) {
 186             Writer writer;
 187             if (csn != null) {
 188                 writer = Channels.newWriter(wbc, csn);
 189             } else {
 190                 writer = Channels.newWriter(wbc, charset);
 191             }
 192 
 193             for (int i = 0; i < ITERATIONS; i++) {
 194                 writer.write(illChars);
 195             }
 196             writer.flush();
 197             writer.close();
 198         }
 199     }
 200 
 201     /**
 202      * Writes the data to a file using a writer created by calling the newWriter
 203      * method.
 204      *
 205      * @param data the data to be written
 206      * @param fileName the file name
 207      * @param csn the charset name
 208      * @param charset the charset
 209      * @throws Exception
 210      */
 211     private void writeToFile(byte[] data, String fileName, String csn, Charset charset) throws Exception {
 212         try (FileOutputStream fos = new FileOutputStream(fileName);
 213                 WritableByteChannel wbc = (WritableByteChannel) fos.getChannel()) {
 214             Writer writer;
 215             String temp;
 216             if (csn != null) {
 217                 writer = Channels.newWriter(wbc, csn);
 218                 temp = new String(data, csn);
 219             } else {
 220                 writer = Channels.newWriter(wbc, charset);
 221                 temp = new String(data, charset);
 222             }
 223 
 224             for (int i = 0; i < ITERATIONS; i++) {
 225                 writer.write(temp);
 226             }
 227             writer.flush();
 228             writer.close();
 229         }
 230     }
 231 
 232     /**
 233      * Reads a file into a String.
 234      *
 235      * @param file the file to be read
 236      * @param csn the charset name
 237      * @param charset the charset
 238      * @throws Exception
 239      */
 240     String readFileToString(String file, String csn, Charset charset) throws Exception {
 241         String result;
 242         try (FileInputStream fis = new FileInputStream(file);
 243                 ReadableByteChannel rbc = (ReadableByteChannel) fis.getChannel()) {
 244             Reader reader;
 245             if (csn != null) {
 246                 reader = Channels.newReader(rbc, csn);
 247             } else {
 248                 reader = Channels.newReader(rbc, charset);
 249             }
 250 
 251             int messageSize = data.length * ITERATIONS;
 252             char data1[] = new char[messageSize];
 253             int totalRead = 0;
 254             int charsRead = 0;
 255             while (totalRead < messageSize) {
 256                 totalRead += charsRead;
 257                 charsRead = reader.read(data1, totalRead, messageSize - totalRead);
 258             }
 259 
 260             result = new String(data1, 0, totalRead);
 261             reader.close();
 262         }
 263 
 264         return result;
 265     }
 266 }