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