1 /*
   2  * Copyright (c) 2018, 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.IOException;
  26 import java.nio.charset.Charset;
  27 import static java.nio.charset.StandardCharsets.US_ASCII;
  28 import static java.nio.charset.StandardCharsets.ISO_8859_1;
  29 import static java.nio.charset.StandardCharsets.UTF_8;
  30 import java.nio.file.Files;
  31 import java.nio.file.OpenOption;
  32 import java.nio.file.Path;
  33 import java.nio.file.Paths;
  34 import java.nio.file.StandardOpenOption;
  35 import static java.nio.file.StandardOpenOption.APPEND;
  36 import java.util.Random;
  37 import java.util.concurrent.Callable;
  38 import static org.testng.Assert.assertTrue;
  39 import static org.testng.Assert.fail;
  40 import org.testng.annotations.AfterClass;
  41 import org.testng.annotations.BeforeClass;
  42 import org.testng.annotations.DataProvider;
  43 import org.testng.annotations.Test;
  44 
  45 /* @test
  46  * @bug 8201276
  47  * @build ReadWriteString PassThroughFileSystem
  48  * @run testng ReadWriteString
  49  * @summary Unit test for methods for Files readString and write methods.
  50  * @key randomness
  51  */
  52 @Test(groups = "readwrite")
  53 public class ReadWriteString {
  54 
  55     private static final OpenOption OPTION_CREATE = StandardOpenOption.CREATE;
  56     // data for text files
  57     private static final String EN_STRING = "The quick brown fox jumps over the lazy dog";
  58     private static final String JA_STRING = "\u65e5\u672c\u8a9e\u6587\u5b57\u5217";
  59     // malformed input: a high surrogate without the low surrogate
  60     static char[] illChars = {
  61         '\u00fa', '\ud800'
  62     };
  63 
  64     static byte[] data = getData();
  65 
  66     static byte[] getData() {
  67         try {
  68             String str1 = "A string that contains ";
  69             String str2 = " , an invalid character for UTF-8.";
  70 
  71             ByteArrayOutputStream baos = new ByteArrayOutputStream();
  72             baos.write(str1.getBytes());
  73             baos.write(0xFA);
  74             baos.write(str2.getBytes());
  75             return baos.toByteArray();
  76         } catch (IOException ex) {
  77             return null; //shouldn't happen
  78         }
  79     }
  80 
  81     // file used by most tests
  82     private Path tmpfile;
  83 
  84 
  85     /*
  86      * DataProvider for malformed write test. Provides the following fields:
  87      * file path, malformed input string, charset
  88      */
  89     @DataProvider(name = "malformedWrite")
  90     public Object[][] getMalformedWrite() throws IOException {
  91         Path path = Files.createTempFile("malformedWrite", null);
  92         return new Object[][]{
  93             {path, "\ud800", null},  //the default Charset is UTF_8
  94             {path, "\u00A0\u00A1", US_ASCII},
  95             {path, "\ud800", UTF_8},
  96             {path, JA_STRING, ISO_8859_1},
  97         };
  98     }
  99 
 100     /*
 101      * DataProvider for illegal input test
 102      * Writes the data in ISO8859 and reads with UTF_8, expects MalformedInputException
 103      */
 104     @DataProvider(name = "illegalInput")
 105     public Object[][] getIllegalInput() throws IOException {
 106         Path path = Files.createTempFile("illegalInput", null);
 107         return new Object[][]{
 108             {path, data, ISO_8859_1, null},
 109             {path, data, ISO_8859_1, UTF_8}
 110         };
 111     }
 112 
 113     @BeforeClass
 114     void setup() throws IOException {
 115         tmpfile = Files.createTempFile("readWriteString", null);
 116     }
 117 
 118     @AfterClass
 119     void cleanup() throws IOException {
 120         Files.deleteIfExists(tmpfile);
 121     }
 122 
 123     /**
 124      * Verifies that NPE is thrown when one of the parameters is null.
 125      */
 126     @Test
 127     public void testNulls() {
 128         Path path = Paths.get(".");
 129         String s = "abc";
 130 
 131         checkNullPointerException(() -> Files.readString((Path) null));
 132         checkNullPointerException(() -> Files.readString((Path) null, UTF_8));
 133         checkNullPointerException(() -> Files.readString(path, (Charset) null));
 134 
 135         checkNullPointerException(() -> Files.writeString((Path) null, s, OPTION_CREATE));
 136         checkNullPointerException(() -> Files.writeString(path, (CharSequence) null, OPTION_CREATE));
 137         checkNullPointerException(() -> Files.writeString(path, s, (OpenOption[]) null));
 138 
 139         checkNullPointerException(() -> Files.writeString((Path) null, s, UTF_8, OPTION_CREATE));
 140         checkNullPointerException(() -> Files.writeString(path, (CharSequence) null, UTF_8, OPTION_CREATE));
 141         checkNullPointerException(() -> Files.writeString(path, s, (Charset) null, OPTION_CREATE));
 142         checkNullPointerException(() -> Files.writeString(path, s, UTF_8, (OpenOption[]) null));
 143     }
 144 
 145     /**
 146      * Verifies the readString and write String methods. Writes to files Strings
 147      * of various sizes, with/without specifying the Charset, and then compares
 148      * the result of reading the files.
 149      */
 150     @Test
 151     public void testReadWrite() throws IOException {
 152         int size = 0;
 153         while (size < 16 * 1024) {
 154             testReadWrite(size, null, false);
 155             testReadWrite(size, null, true);
 156             testReadWrite(size, UTF_8, false);
 157             testReadWrite(size, UTF_8, true);
 158             size += 1024;
 159         }
 160     }
 161 
 162     /**
 163      * Verifies that IOException is thrown (as specified) when giving a malformed
 164      * string input.
 165      *
 166      * @param path the path to write
 167      * @param s the string
 168      * @param cs the Charset
 169      * @throws IOException if the input is malformed
 170      */
 171     @Test(dataProvider = "malformedWrite", expectedExceptions = IOException.class)
 172     public void testMalformedWrite(Path path, String s, Charset cs) throws IOException {
 173         path.toFile().deleteOnExit();
 174         if (cs == null) {
 175             Files.writeString(path, s, OPTION_CREATE);
 176         } else {
 177             Files.writeString(path, s, cs, OPTION_CREATE);
 178         }
 179     }
 180 
 181     /**
 182      * Verifies that IOException is thrown when reading a file using the wrong
 183      * Charset.
 184      *
 185      * @param path the path to write and read
 186      * @param data the data used for the test
 187      * @param csWrite the Charset to use for writing the test file
 188      * @param csRead the Charset to use for reading the file
 189      * @throws IOException when the Charset used for reading the file is incorrect
 190      */
 191     @Test(dataProvider = "illegalInput", expectedExceptions = IOException.class)
 192     public void testMalformedRead(Path path, byte[] data, Charset csWrite, Charset csRead) throws IOException {
 193         path.toFile().deleteOnExit();
 194         String temp = new String(data, csWrite);
 195         Files.writeString(path, temp, csWrite, OPTION_CREATE);
 196         String s;
 197         if (csRead == null) {
 198             s = Files.readString(path);
 199         } else {
 200             s = Files.readString(path, csRead);
 201         }
 202     }
 203 
 204     private void checkNullPointerException(Callable<?> c) {
 205         try {
 206             c.call();
 207             fail("NullPointerException expected");
 208         } catch (NullPointerException ignore) {
 209         } catch (Exception e) {
 210             fail(e + " not expected");
 211         }
 212     }
 213 
 214     private void testReadWrite(int size, Charset cs, boolean append) throws IOException {
 215         StringBuilder sb = new StringBuilder(size);
 216         String expected;
 217         String str = generateString(size);
 218         Path result;
 219         if (cs == null) {
 220             result = Files.writeString(tmpfile, str);
 221         } else {
 222             result = Files.writeString(tmpfile, str, cs);
 223         }
 224 
 225         //System.out.println(result.toUri().toASCIIString());
 226         assertTrue(result == tmpfile);
 227         if (append) {
 228             if (cs == null) {
 229                 Files.writeString(tmpfile, str, APPEND);
 230             } else {
 231                 Files.writeString(tmpfile, str, cs, APPEND);
 232             }
 233             assertTrue(Files.size(tmpfile) == size * 2);
 234         }
 235 
 236 
 237         if (append) {
 238             sb.append(str).append(str);
 239             expected = sb.toString();
 240         } else {
 241             expected = str;
 242         }
 243 
 244         String read;
 245         if (cs == null) {
 246             read = Files.readString(result);
 247         } else {
 248             read = Files.readString(result, cs);
 249         }
 250         //System.out.println("chars read: " + read.length());
 251         //System.out.println(read);
 252         //System.out.println("---end---");
 253         assertTrue(read.equals(expected), "String read not the same as written");
 254     }
 255 
 256     static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz \r\n".toCharArray();
 257     StringBuilder sb = new StringBuilder(512);
 258     Random random = new Random();
 259 
 260     private String generateString(int size) {
 261         sb.setLength(0);
 262         for (int i = 0; i < size; i++) {
 263             char c = CHARS[random.nextInt(CHARS.length)];
 264             sb.append(c);
 265         }
 266 
 267         return sb.toString();
 268     }
 269 }