1 /*
   2  * Copyright (c) 2011, 2013, 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 /* @test
  25  * @bug 7006126 8020669 8024788 8019526
  26  * @build BytesAndLines PassThroughFileSystem
  27  * @run testng BytesAndLines
  28  * @summary Unit test for methods for Files readAllBytes, readAllLines and
  29  *     and write methods.
  30  * @key randomness
  31  */
  32 
  33 import java.nio.ByteBuffer;
  34 import java.nio.CharBuffer;
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.Paths;
  38 import java.nio.file.OpenOption;
  39 import static java.nio.file.StandardOpenOption.*;
  40 import java.nio.charset.Charset;
  41 import java.nio.charset.CharacterCodingException;
  42 import java.nio.charset.MalformedInputException;
  43 import java.nio.charset.UnmappableCharacterException;
  44 import static java.nio.charset.StandardCharsets.*;
  45 import java.util.Arrays;
  46 import java.util.ArrayList;
  47 import java.util.Collections;
  48 import java.util.List;
  49 import java.util.Random;
  50 import java.util.concurrent.Callable;
  51 import java.io.IOException;
  52 
  53 import org.testng.annotations.AfterClass;
  54 import org.testng.annotations.BeforeClass;
  55 import org.testng.annotations.Test;
  56 import static org.testng.Assert.*;
  57 
  58 @Test(groups = "unit")
  59 public class BytesAndLines {
  60 
  61     // data for text files
  62     private static final String EN_STRING = "The quick brown fox jumps over the lazy dog";
  63     private static final String JA_STRING = "\u65e5\u672c\u8a9e\u6587\u5b57\u5217";
  64 
  65     // used for random byte content
  66     private static Random RAND = new Random();
  67 
  68     // file used by most tests
  69     private Path tmpfile;
  70 
  71     @BeforeClass
  72     void setup() throws IOException {
  73         tmpfile = Files.createTempFile("blah", null);
  74     }
  75 
  76     @AfterClass
  77     void cleanup() throws IOException {
  78         Files.deleteIfExists(tmpfile);
  79     }
  80 
  81     /**
  82      * Returns a byte[] of the given size with random content
  83      */
  84     private byte[] genBytes(int size) {
  85         byte[] arr = new byte[size];
  86         RAND.nextBytes(arr);
  87         return arr;
  88     }
  89 
  90     /**
  91      * Exercise NullPointerException
  92      */
  93     public void testNulls() {
  94         Path file = Paths.get("foo");
  95         byte[] bytes = new byte[100];
  96         List<String> lines = Collections.emptyList();
  97 
  98         checkNullPointerException(() -> Files.readAllBytes(null));
  99 
 100         checkNullPointerException(() -> Files.write(null, bytes));
 101         checkNullPointerException(() -> Files.write(file, (byte[])null));
 102         checkNullPointerException(() -> Files.write(file, bytes, (OpenOption[])null));
 103         checkNullPointerException(() -> Files.write(file, bytes, new OpenOption[] { null } ));
 104 
 105         checkNullPointerException(() -> Files.readAllLines(null));
 106         checkNullPointerException(() -> Files.readAllLines(file, (Charset)null));
 107         checkNullPointerException(() -> Files.readAllLines(null, Charset.defaultCharset()));
 108 
 109         checkNullPointerException(() -> Files.write(null, lines));
 110         checkNullPointerException(() -> Files.write(file, (List<String>)null));
 111         checkNullPointerException(() -> Files.write(file, lines, (OpenOption[])null));
 112         checkNullPointerException(() -> Files.write(file, lines, new OpenOption[] { null } ));
 113         checkNullPointerException(() -> Files.write(null, lines, Charset.defaultCharset()));
 114         checkNullPointerException(() -> Files.write(file, null, Charset.defaultCharset()));
 115         checkNullPointerException(() -> Files.write(file, lines, (Charset)null));
 116         checkNullPointerException(() -> Files.write(file, lines, Charset.defaultCharset(), (OpenOption[])null));
 117         checkNullPointerException(() -> Files.write(file, lines, Charset.defaultCharset(), new OpenOption[] { null } ));
 118     }
 119 
 120     private void checkNullPointerException(Callable<?> c) {
 121         try {
 122             c.call();
 123             fail("NullPointerException expected");
 124         } catch (NullPointerException ignore) {
 125         } catch (Exception e) {
 126             fail(e + " not expected");
 127         }
 128     }
 129 
 130     /**
 131      * Exercise Files.readAllBytes(Path) on varied file sizes
 132      */
 133     public void testReadAllBytes() throws IOException {
 134         int size = 0;
 135         while (size <= 16*1024) {
 136             testReadAllBytes(size);
 137             size += 512;
 138         }
 139     }
 140 
 141     private void testReadAllBytes(int size) throws IOException {
 142         // write bytes to file (random content)
 143         byte[] expected = genBytes(size);
 144         Files.write(tmpfile, expected);
 145 
 146         // check expected bytes are read
 147         byte[] read = Files.readAllBytes(tmpfile);
 148         assertTrue(Arrays.equals(read, expected), "Bytes read not the same as written");
 149     }
 150 
 151     /**
 152      * Linux specific test to exercise Files.readAllBytes on /proc. This is
 153      * special because file sizes are reported as 0 even though the file
 154      * has content.
 155      */
 156     public void testReadAllBytesOnProcFS() throws IOException {
 157         // read from procfs
 158         if (System.getProperty("os.name").equals("Linux")) {
 159             Path statFile = Paths.get("/proc/self/stat");
 160             byte[] data = Files.readAllBytes(statFile);
 161             assertTrue(data.length > 0, "Files.readAllBytes('" + statFile + "') failed to read");
 162         }
 163     }
 164 
 165     /**
 166      * Exercise Files.readAllBytes(Path) on custom file system. This is special
 167      * because readAllBytes was originally implemented to use FileChannel
 168      * and so may not be supported by custom file system providers.
 169      */
 170     public void testReadAllBytesOnCustomFS() throws IOException {
 171         Path myfile = PassThroughFileSystem.create().getPath("myfile");
 172         try {
 173             int size = 0;
 174             while (size <= 1024) {
 175                 byte[] b1 = genBytes(size);
 176                 Files.write(myfile, b1);
 177                 byte[] b2 = Files.readAllBytes(myfile);
 178                 assertTrue(Arrays.equals(b1, b2), "bytes not equal");
 179                 size += 512;
 180             }
 181         } finally {
 182             Files.deleteIfExists(myfile);
 183         }
 184     }
 185 
 186     /**
 187      * Exercise Files.write(Path, byte[], OpenOption...) on various sizes
 188      */
 189     public void testWriteBytes() throws IOException {
 190         int size = 0;
 191         while (size < 16*1024) {
 192             testWriteBytes(size, false);
 193             testWriteBytes(size, true);
 194             size += 512;
 195         }
 196     }
 197 
 198     private void testWriteBytes(int size, boolean append) throws IOException {
 199         byte[] bytes = genBytes(size);
 200         Path result = Files.write(tmpfile, bytes);
 201         assertTrue(result == tmpfile);
 202         if (append) {
 203             Files.write(tmpfile, bytes, APPEND);
 204             assertTrue(Files.size(tmpfile) == size*2);
 205         }
 206 
 207         byte[] expected;
 208         if (append) {
 209             expected = new byte[size << 1];
 210             System.arraycopy(bytes, 0, expected, 0, bytes.length);
 211             System.arraycopy(bytes, 0, expected, bytes.length, bytes.length);
 212         } else {
 213             expected = bytes;
 214         }
 215 
 216         byte[] read = Files.readAllBytes(tmpfile);
 217         assertTrue(Arrays.equals(read, expected), "Bytes read not the same as written");
 218     }
 219 
 220     /**
 221      * Exercise Files.readAllLines(Path, Charset)
 222      */
 223     public void testReadAllLines() throws IOException {
 224         // zero lines
 225         Files.write(tmpfile, new byte[0]);
 226         List<String> lines = Files.readAllLines(tmpfile, US_ASCII);
 227             assertTrue(lines.isEmpty(), "No line expected");
 228 
 229         // one line
 230         byte[] hi = { (byte)'h', (byte)'i' };
 231         Files.write(tmpfile, hi);
 232         lines = Files.readAllLines(tmpfile, US_ASCII);
 233         assertTrue(lines.size() == 1, "One line expected");
 234         assertTrue(lines.get(0).equals("hi"), "'Hi' expected");
 235 
 236         // two lines using platform's line separator
 237         List<String> expected = Arrays.asList("hi", "there");
 238         Files.write(tmpfile, expected, US_ASCII);
 239         assertTrue(Files.size(tmpfile) > 0, "File is empty");
 240         lines = Files.readAllLines(tmpfile, US_ASCII);
 241         assertTrue(lines.equals(expected), "Unexpected lines");
 242 
 243         // MalformedInputException
 244         byte[] bad = { (byte)0xff, (byte)0xff };
 245         Files.write(tmpfile, bad);
 246         try {
 247             Files.readAllLines(tmpfile, US_ASCII);
 248             fail("MalformedInputException expected");
 249         } catch (MalformedInputException ignore) { }
 250     }
 251 
 252     /**
 253      * Linux specific test to exercise Files.readAllLines(Path) on /proc. This
 254      * is special because file sizes are reported as 0 even though the file
 255      * has content.
 256      */
 257     public void testReadAllLinesOnProcFS() throws IOException {
 258         if (System.getProperty("os.name").equals("Linux")) {
 259             Path statFile = Paths.get("/proc/self/stat");
 260             List<String> lines = Files.readAllLines(statFile);
 261             assertTrue(lines.size() > 0, "Files.readAllLines('" + statFile + "') failed to read");
 262         }
 263     }
 264 
 265     /**
 266      * Exercise Files.readAllLines(Path)
 267      */
 268     public void testReadAllLinesUTF8() throws IOException {
 269         Files.write(tmpfile, encodeAsUTF8(EN_STRING + "\n" + JA_STRING));
 270 
 271         List<String> lines = Files.readAllLines(tmpfile);
 272         assertTrue(lines.size() == 2, "Read " + lines.size() + " lines instead of 2");
 273         assertTrue(lines.get(0).equals(EN_STRING));
 274         assertTrue(lines.get(1).equals(JA_STRING));
 275 
 276         // a sample of malformed sequences
 277         testReadAllLinesMalformedUTF8((byte)0xFF); // one-byte sequence
 278         testReadAllLinesMalformedUTF8((byte)0xC0, (byte)0x80);  // invalid first byte
 279         testReadAllLinesMalformedUTF8((byte)0xC2, (byte)0x00); // invalid second byte
 280     }
 281 
 282     private byte[] encodeAsUTF8(String s) throws CharacterCodingException {
 283         // not using s.getBytes here so as to catch unmappable characters
 284         ByteBuffer bb = UTF_8.newEncoder().encode(CharBuffer.wrap(s));
 285         byte[] result = new byte[bb.limit()];
 286         bb.get(result);
 287         assertTrue(bb.remaining() == 0);
 288         return result;
 289     }
 290 
 291     private void testReadAllLinesMalformedUTF8(byte... bytes) throws IOException {
 292         Files.write(tmpfile, bytes);
 293         try {
 294             Files.readAllLines(tmpfile);
 295             fail("MalformedInputException expected");
 296         } catch (MalformedInputException ignore) { }
 297     }
 298 
 299     /**
 300      * Exercise Files.write(Path, Iterable<? extends CharSequence>, Charset, OpenOption...)
 301      */
 302     public void testWriteLines() throws IOException {
 303         // zero lines
 304         Path result = Files.write(tmpfile, Collections.<String>emptyList(), US_ASCII);
 305         assert(Files.size(tmpfile) == 0);
 306         assert(result == tmpfile);
 307 
 308         // two lines
 309         List<String> lines = Arrays.asList("hi", "there");
 310         Files.write(tmpfile, lines, US_ASCII);
 311         List<String> actual = Files.readAllLines(tmpfile, US_ASCII);
 312         assertTrue(actual.equals(lines), "Unexpected lines");
 313 
 314         // append two lines
 315         Files.write(tmpfile, lines, US_ASCII, APPEND);
 316         List<String> expected = new ArrayList<>();
 317         expected.addAll(lines);
 318         expected.addAll(lines);
 319         assertTrue(expected.size() == 4, "List should have 4 elements");
 320         actual = Files.readAllLines(tmpfile, US_ASCII);
 321         assertTrue(actual.equals(expected), "Unexpected lines");
 322 
 323         // UnmappableCharacterException
 324         try {
 325             String s = "\u00A0\u00A1";
 326             Files.write(tmpfile, Arrays.asList(s), US_ASCII);
 327             fail("UnmappableCharacterException expected");
 328         } catch (UnmappableCharacterException ignore) { }
 329     }
 330 
 331     /**
 332      * Exercise Files.write(Path, Iterable<? extends CharSequence>, OpenOption...)
 333      */
 334     public void testWriteLinesUTF8() throws IOException {
 335         List<String> lines = Arrays.asList(EN_STRING, JA_STRING);
 336         Files.write(tmpfile, lines);
 337         List<String> actual = Files.readAllLines(tmpfile, UTF_8);
 338         assertTrue(actual.equals(lines), "Unexpected lines");
 339     }
 340 }