1 /*
   2  * Copyright (c) 2000, 2012, 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 6191269 6709457 8000330
  26  * @summary Test truncate method of FileChannel
  27  * @key randomness
  28  */
  29 
  30 import java.io.*;
  31 import java.nio.ByteBuffer;
  32 import java.nio.channels.*;
  33 import java.nio.file.Files;
  34 import static java.nio.file.StandardOpenOption.*;
  35 import static java.nio.charset.StandardCharsets.*;
  36 import java.util.Random;
  37 
  38 
  39 /**
  40  * Testing FileChannel's truncate method.
  41  */
  42 
  43 public class Truncate {
  44     private static final Random generator = new Random();
  45 
  46     public static void main(String[] args) throws Exception {
  47         File blah = File.createTempFile("blah", null);
  48         blah.deleteOnExit();
  49         try {
  50             basicTest(blah);
  51             appendTest(blah);
  52             exceptionTests(blah);
  53         } finally {
  54             blah.delete();
  55         }
  56     }
  57 
  58     /**
  59      * Basic test of asserts in truncate's specification.
  60      */
  61     static void basicTest(File blah) throws Exception {
  62         for(int i=0; i<100; i++) {
  63             long testSize = generator.nextInt(1000) + 10;
  64             initTestFile(blah, testSize);
  65 
  66             try (FileChannel fc = (i < 50) ?
  67                  new RandomAccessFile(blah, "rw").getChannel() :
  68                  FileChannel.open(blah.toPath(), READ, WRITE))
  69                 {
  70                     if (fc.size() != testSize)
  71                         throw new RuntimeException("Size failed");
  72 
  73                     long position = generator.nextInt((int)testSize*2);
  74                     fc.position(position);
  75 
  76                     long newSize = generator.nextInt((int)testSize*2);
  77                     fc.truncate(newSize);
  78 
  79                     // check new size
  80                     if (newSize > testSize) {
  81                         if (fc.size() != testSize)
  82                             throw new RuntimeException("Attempt to expand file changed size");
  83                     } else {
  84                         if (fc.size() != newSize)
  85                             throw new RuntimeException("Unexpected size after truncate");
  86                     }
  87 
  88                     // check new position
  89                     if (position > newSize) {
  90                         if (fc.position() != newSize)
  91                             throw new RuntimeException("Position greater than size");
  92                     } else {
  93                         if (fc.position() != position)
  94                             throw new RuntimeException("Truncate changed position");
  95                     };
  96                 }
  97         }
  98     }
  99 
 100     /**
 101      * Test behavior of truncate method when file is opened for append
 102      */
 103     static void appendTest(File blah) throws Exception {
 104         for (int i=0; i<10; i++) {
 105             long testSize = generator.nextInt(1000) + 10;
 106             initTestFile(blah, testSize);
 107             try (FileChannel fc = (i < 5) ?
 108                  new FileOutputStream(blah, true).getChannel() :
 109                  FileChannel.open(blah.toPath(), APPEND))
 110                 {
 111                     // truncate file
 112                     long newSize = generator.nextInt((int)testSize);
 113                     fc.truncate(newSize);
 114                     if (fc.size() != newSize)
 115                         throw new RuntimeException("Truncate failed");
 116 
 117                     // write one byte
 118                     ByteBuffer buf = ByteBuffer.allocate(1);
 119                     buf.put((byte)'x');
 120                     buf.flip();
 121                     fc.write(buf);
 122                     if (fc.size() != (newSize+1))
 123                         throw new RuntimeException("Unexpected size");
 124                 }
 125         }
 126     }
 127 
 128     /**
 129      * Test exceptions specified by truncate method
 130      */
 131     static void exceptionTests(File blah) throws Exception {
 132         // check exceptions when channel opened for read access
 133         try (FileChannel fc = FileChannel.open(blah.toPath(), READ)) {
 134             long size = fc.size();
 135 
 136             // open channel
 137             checkException(fc, 0L, NonWritableChannelException.class);
 138 
 139             checkException(fc, -1L, NonWritableChannelException.class,
 140                            IllegalArgumentException.class);
 141 
 142             checkException(fc, size+1L, NonWritableChannelException.class);
 143 
 144             // closed channel
 145             fc.close();
 146 
 147             checkException(fc, 0L, ClosedChannelException.class);
 148 
 149             checkException(fc, -1L, ClosedChannelException.class,
 150                            IllegalArgumentException.class);
 151 
 152             checkException(fc, size+1L, ClosedChannelException.class);
 153         }
 154 
 155         // check exceptions when channel opened for write access
 156         try (FileChannel fc = FileChannel.open(blah.toPath(), WRITE)) {
 157             long size = fc.size();
 158 
 159             // open channel
 160             checkException(fc, -1L, IllegalArgumentException.class);
 161 
 162             // closed channel
 163             fc.close();
 164 
 165             checkException(fc, 0L, ClosedChannelException.class);
 166 
 167             checkException(fc, -1L, ClosedChannelException.class,
 168                            IllegalArgumentException.class);
 169 
 170             checkException(fc, size+1L, ClosedChannelException.class);
 171         }
 172     }
 173 
 174     /**
 175      * Checks that FileChannel truncate throws one of the expected exceptions
 176      * when invoked with the given size.
 177      */
 178     private static void checkException(FileChannel fc, long size, Class<?>... expected)
 179         throws IOException
 180     {
 181         Exception exc = null;
 182         try {
 183             fc.truncate(size);
 184         } catch (Exception actual) {
 185             exc = actual;
 186         }
 187         if (exc != null) {
 188             for (Class<?> clazz: expected) {
 189                 if (clazz.isInstance(exc)) {
 190                     return;
 191                 }
 192             }
 193         }
 194         System.err.println("Expected one of");
 195         for (Class<?> clazz: expected) {
 196             System.err.println(clazz);
 197         }
 198         if (exc == null) {
 199             throw new RuntimeException("No expection thrown");
 200         } else {
 201             throw new RuntimeException("Unexpected exception thrown", exc);
 202         }
 203     }
 204 
 205     /**
 206      * Creates file blah of specified size in bytes.
 207      */
 208     private static void initTestFile(File blah, long size) throws Exception {
 209         try (BufferedWriter writer = Files.newBufferedWriter(blah.toPath(), ISO_8859_1)) {
 210             for(int i=0; i<size; i++) {
 211                 writer.write("e");
 212             }
 213         }
 214     }
 215 }