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