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 
main(String[] args)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      */
basicTest(File blah)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      */
appendTest(File blah)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      */
exceptionTests(File blah)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      */
checkException(FileChannel fc, long size, Class<?>... expected)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      */
initTestFile(File blah, long size)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 }
216